From 5b25a5d1cff6210cd171e96e5bd0519b0ac1af83 Mon Sep 17 00:00:00 2001 From: "FiveFilters.org" Date: Tue, 31 Aug 2021 23:46:39 +0200 Subject: Add more tests from Readability.js --- test/test-pages/mercurial/expected-images.json | 1 + test/test-pages/mercurial/expected-metadata.json | 8 + test/test-pages/mercurial/expected.html | 738 ++++ test/test-pages/mercurial/source.html | 979 ++++++ test/test-pages/nytimes-4/expected-images.json | 4 + test/test-pages/nytimes-4/expected-metadata.json | 8 + test/test-pages/nytimes-4/expected.html | 214 ++ test/test-pages/nytimes-4/source.html | 3896 ++++++++++++++++++++++ 8 files changed, 5848 insertions(+) create mode 100644 test/test-pages/mercurial/expected-images.json create mode 100644 test/test-pages/mercurial/expected-metadata.json create mode 100644 test/test-pages/mercurial/expected.html create mode 100644 test/test-pages/mercurial/source.html create mode 100644 test/test-pages/nytimes-4/expected-images.json create mode 100644 test/test-pages/nytimes-4/expected-metadata.json create mode 100644 test/test-pages/nytimes-4/expected.html create mode 100644 test/test-pages/nytimes-4/source.html (limited to 'test/test-pages') diff --git a/test/test-pages/mercurial/expected-images.json b/test/test-pages/mercurial/expected-images.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/test/test-pages/mercurial/expected-images.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/test-pages/mercurial/expected-metadata.json b/test/test-pages/mercurial/expected-metadata.json new file mode 100644 index 0000000..d998fb4 --- /dev/null +++ b/test/test-pages/mercurial/expected-metadata.json @@ -0,0 +1,8 @@ +{ + "Author": null, + "Direction": null, + "Excerpt": "Contents", + "Image": null, + "Title": "Shared Mutable History \u2014 evolve extension for Mercurial", + "SiteName": null +} \ No newline at end of file diff --git a/test/test-pages/mercurial/expected.html b/test/test-pages/mercurial/expected.html new file mode 100644 index 0000000..61915e4 --- /dev/null +++ b/test/test-pages/mercurial/expected.html @@ -0,0 +1,738 @@ +
+ + +

+ Once you have mastered the art of mutable history in a single repository (see the user guide), you can move up to the next level: shared mutable history. evolve lets you push and pull draft changesets between repositories along with their obsolescence markers. This opens up a number of interesting possibilities. +

+

+ The simplest scenario is a single developer working across two computers. Say you’re working on code that must be tested on a remote test server, probably in a rack somewhere, only accessible by SSH, and running an “enterprise-grade” (out-of-date) OS. But you probably prefer to write code locally: everything is setup the way you like it, and you can use your preferred editor, IDE, merge/diff tools, etc. +

+

+ Traditionally, your options are limited: either +

+
+
+
    +
  • (ab)use your source control system by committing half-working code in order to get it onto the remote test server, or +
  • +
  • go behind source control’s back by using rsync (or similar) to transfer your code back-and-forth until it is ready to commit +
  • +
+
+
+

+ The former is less bad with distributed version control systems like Mercurial, but it’s still far from ideal. (One important version control “best practice” is that every commit should make things just a little bit better, i.e. you should never commit code that is worse than what came before.) The latter, avoiding version control entirely, means that you’re walking a tightrope without a safety net. One accidental rsync in the wrong direction could destroy hours of work. +

+

+ Using Mercurial with evolve to share mutable history solves these problems. As with single-repository evolve, you can commit whenever the code is demonstrably better, even if all the tests aren’t passing yet—just hg amend when they are. And you can transfer those half-baked changesets between repositories to try things out on your test server before anything is carved in stone. +

+

+ A less common scenario is multiple developers sharing mutable history, typically for code review. We’ll cover this scenario later. First, we will cover single-user sharing. +

+
+

+ Sharing with a single developer +

+
+

+ Publishing and non-publishing repositories +

+

+ The key to shared mutable history is to keep your changesets in draft phase as you pass them around. Recall that by default, hg push promotes changesets from draft to public, and public changesets are immutable. You can change this behaviour by reconfiguring the remote repository so that it is non-publishing. (Short version: set phases.publish to false. Long version follows.) +

+
+
+

+ Setting up +

+

+ We’ll work through an example with three local repositories, although in the real world they’d most likely be on three different computers. First, the public repository is where tested, polished changesets live, and it is where you synchronize with the rest of your team. +

+ +

+ We’ll need two clones where work gets done, test-repo and dev-repo: +

+
+
$ hg clone public test-repo
+updating to branch default
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+$ hg clone test-repo dev-repo
+updating to branch default
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+

+ dev-repo is your local machine, with GUI merge tools and IDEs and everything configured just the way you like it. test-repo is the test server in a rack somewhere behind SSH. So for the most part, we’ll develop in dev-repo, push to test-repo, test and polish there, and push to public. +

+

+ The key to shared mutable history is to make the target repository, in this case test-repo, non-publishing. And, of course, we have to enable the evolve extension in both test-repo and dev-repo. +

+

+ First, edit the configuration for test-repo: +

+
+
$ hg -R test-repo config --edit --local
+
+
+

+ and add +

+
+
[phases]
+publish = false
+
+[extensions]
+evolve =
+
+
+

+ Then edit the configuration for dev-repo: +

+
+
$ hg -R dev-repo config --edit --local
+
+
+

+ and add +

+ +

+ Keep in mind that in real life, these repositories would probably be on separate computers, so you’d have to login to each one to configure each repository. +

+

+ To start things off, let’s make one public, immutable changeset: +

+
+
$ cd test-repo
+$ echo 'my new project' > file1
+$ hg add file1
+$ hg commit -m 'create new project'
+$ hg push
+[...]
+added 1 changesets with 1 changes to 1 files
+
+
+

+ and pull that into the development repository: +

+
+
$ cd ../dev-repo
+$ hg pull -u
+[...]
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+
+ +
+

+ Example 2: Amend again, locally +

+

+ This process can repeat. Perhaps you figure out a more elegant fix to the bug, and want to mutate history so nobody ever knows you had a less-than-perfect idea. We’ll implement it locally in dev-repo and push to test-repo: +

+
+
$ echo 'Fix, fix, and fix.' > file1
+$ hg amend
+$ hg push
+
+
+

+ This time around, the temporary amend commit is in dev-repo, and it is not transferred to test-repo—the same as before, just in the opposite direction. Figure 4 shows the two repositories after amending in dev-repo and pushing to test-repo. +

+
+

+ [figure SG04: each repo has one temporary amend commit, but they’re different in each one] +

+
+

+ Let’s hop over to test-repo to test the more elegant fix: +

+
+
$ cd ../test-repo
+$ hg update
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+

+ This time, all the tests pass, so no further amending is required. This bug fix is finished, so we push it to the public repository: +

+
+
$ hg push
+[...]
+added 1 changesets with 1 changes to 1 files
+
+
+

+ Note that only one changeset—the final version, after two amendments—was actually pushed. Again, Mercurial doesn’t transfer hidden changesets on push and pull. +

+

+ So the picture in public is much simpler than in either dev-repo or test-repo. Neither of our missteps nor our amendments are publicly visible, just the final, beautifully polished changeset: +

+
+

+ [figure SG05: public repo with rev 0:0dc9, 1:de61, both public] +

+
+

+ There is one important step left to do. Because we pushed from test-repo to public, the pushed changeset is in public phase in those two repositories. But dev-repo has been out-of-the-loop; changeset de61 is still draft there. If we’re not careful, we might mutate history in dev-repo, obsoleting a changeset that is already public. Let’s avoid that situation for now by pushing up to dev-repo: +

+
+
$ hg push ../dev-repo
+pushing to ../dev-repo
+searching for changes
+no changes found
+
+
+

+ Even though no changesets were pushed, Mercurial still pushed obsolescence markers and phase changes to dev-repo. +

+

+ A final note: since this fix is now public, it is immutable. It’s no longer possible to amend it: +

+
+
$ hg amend -m 'fix bug 37'
+abort: cannot amend public changesets
+
+
+

+ This is, after all, the whole point of Mercurial’s phases: to prevent rewriting history that has already been published. +

+
+
+
+

+ Sharing with multiple developers: code review +

+

+ Now that you know how to share your own mutable history across multiple computers, you might be wondering if it makes sense to share mutable history with others. It does, but you have to be careful, stay alert, and communicate with your peers. +

+

+ Code review is a good use case for sharing mutable history across multiple developers: Alice commits a draft changeset, submits it for review, and amends her changeset until her reviewer is satisfied. Meanwhile, Bob is also committing draft changesets for review, amending until his reviewer is satisfied. Once a particular changeset passes review, the respective author (Alice or Bob) pushes it to the public (publishing) repository. +

+

+ Incidentally, the reviewers here can be anyone: maybe Bob and Alice review each other’s work; maybe the same third party reviews both; or maybe they pick different experts to review their work on different parts of a large codebase. Similarly, it doesn’t matter if reviews are conducted in person, by email, or by carrier pigeon. Code review is outside of the scope of Mercurial, so all we’re looking at here is the mechanics of committing, amending, pushing, and pulling. +

+
+

+ Setting up +

+

+ To demonstrate, let’s start with the public repository as we left it in the last example, with two immutable changesets (figure 5 above). We’ll clone a review repository from it, and then Alice and Bob will both clone from review. +

+
+
$ hg clone public review
+updating to branch default
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+$ hg clone review alice
+updating to branch default
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+$ hg clone review bob
+updating to branch default
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+

+ We need to configure Alice’s and Bob’s working repositories to enable evolve. First, edit Alice’s configuration with +

+
+
$ hg -R alice config --edit --local
+
+
+

+ and add +

+ +

+ Then edit Bob’s repository configuration: +

+
+
$ hg -R bob config --edit --local
+
+
+

+ and add the same text. +

+
+
+

+ Example 3: Alice commits and amends a draft fix +

+

+ We’ll follow Alice working on a bug fix. We’re going to use bookmarks to make it easier to understand multiple branch heads in the review repository, so Alice starts off by creating a bookmark and committing her first attempt at a fix: +

+
+
$ hg bookmark bug15
+$ echo 'fix' > file2
+$ hg commit -A -u alice -m 'fix bug 15 (v1)'
+adding file2
+
+
+

+ Note the unorthodox “(v1)” in the commit message. We’re just using that to make this tutorial easier to follow; it’s not something we’d recommend in real life. +

+

+ Of course Alice wouldn’t commit unless her fix worked to her satisfaction, so it must be time to solicit a code review. She does this by pushing to the review repository: +

+
+
$ hg push -B bug15
+[...]
+added 1 changesets with 1 changes to 1 files
+exporting bookmark bug15
+
+
+

+ (The use of -B is important to ensure that we only push the bookmarked head, and that the bookmark itself is pushed. See this guide to bookmarks, especially the Sharing Bookmarks section, if you’re not familiar with bookmarks.) +

+

+ Some time passes, and Alice receives her code review. As a result, Alice revises her fix and submits it for a second review: +

+
+
$ echo 'Fix.' > file2
+$ hg amend -m 'fix bug 15 (v2)'
+$ hg push
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+updating bookmark bug15
+
+
+

+ Figure 6 shows the state of the review repository at this point. +

+
+

+ [figure SG06: rev 2:fn1e is Alice’s obsolete v1, rev 3:cbdf is her v2; both children of rev 1:de61] +

+
+

+ After a busy morning of bug fixing, Alice stops for lunch. Let’s see what Bob has been up to. +

+
+
+

+ Example 4: Bob implements and publishes a new feature +

+

+ Meanwhile, Bob has been working on a new feature. Like Alice, he’ll use a bookmark to track his work, and he’ll push that bookmark to the review repository, so that reviewers know which changesets to review. +

+
+
$ cd ../bob
+$ echo 'stuff' > file1
+$ hg bookmark featureX
+$ hg commit -u bob -m 'implement feature X (v1)'          # rev 4:1636
+$ hg push -B featureX
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+exporting bookmark featureX
+
+
+

+ When Bob receives his code review, he improves his implementation a bit, amends, and submits the resulting changeset for review: +

+
+
$ echo 'do stuff' > file1
+$ hg amend -m 'implement feature X (v2)'                  # rev 5:0eb7
+$ hg push
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+updating bookmark featureX
+
+
+

+ Unfortunately, that still doesn’t pass muster. Bob’s reviewer insists on proper capitalization and punctuation. +

+
+
$ echo 'Do stuff.' > file1
+$ hg amend -m 'implement feature X (v3)'                  # rev 6:540b
+
+
+

+ On the bright side, the second review said, “Go ahead and publish once you fix that.” So Bob immediately publishes his third attempt: +

+
+
$ hg push ../public
+[...]
+added 1 changesets with 1 changes to 1 files
+
+
+

+ It’s not enough just to update public, though! Other people also use the review repository, and right now it doesn’t have Bob’s latest amendment (“v3”, revision 6:540b), nor does it know that the precursor of that changeset (“v2”, revision 5:0eb7) is obsolete. Thus, Bob pushes to review as well: +

+
+
$ hg push ../review
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+updating bookmark featureX
+
+
+

+ Figure 7 shows the result of Bob’s work in both review and public. +

+
+

+ [figure SG07: review includes Alice’s draft work on bug 15, as well as Bob’s v1, v2, and v3 changes for feature X: v1 and v2 obsolete, v3 public. public contains only the final, public implementation of feature X] +

+
+

+ Incidentally, it’s important that Bob push to public before review. If he pushed to review first, then revision 6:540b would still be in draft phase in review, but it would be public in both Bob’s local repository and the public repository. That could lead to confusion at some point, which is easily avoided by pushing first to public. +

+
+
+

+ Example 5: Alice integrates and publishes +

+

+ Finally, Alice gets back from lunch and sees that the carrier pigeon with her second review has arrived (or maybe it’s in her email inbox). Alice’s reviewer approved her amended changeset, so she pushes it to public: +

+
+
$ hg push ../public
+[...]
+remote has heads on branch 'default' that are not known locally: 540ba8f317e6
+abort: push creates new remote head cbdfbd5a5db2!
+(pull and merge or see "hg help push" for details about pushing new heads)
+
+
+

+ Oops! Bob has won the race to push first to public. So Alice needs to integrate with Bob: let’s pull his changeset(s) and see what the branch heads are. +

+
+
$ hg pull ../public
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+(run 'hg heads' to see heads, 'hg merge' to merge)
+$ hg log -G -q -r 'head()' --template '{rev}:{node|short}  ({author})\n'
+o  5:540ba8f317e6  (bob)
+|
+| @  4:cbdfbd5a5db2  (alice)
+|/
+
+
+

+ We’ll assume Alice and Bob are perfectly comfortable with rebasing changesets. (After all, they’re already using mutable history in the form of amend.) So Alice rebases her changeset on top of Bob’s and publishes the result: +

+
+
$ hg rebase -d 5
+$ hg push ../public
+[...]
+added 1 changesets with 1 changes to 1 files
+$ hg push ../review
+[...]
+added 1 changesets with 0 changes to 0 files
+updating bookmark bug15
+
+
+

+ The result, in both review and public repositories, is shown in figure 8. +

+
+

+ [figure SG08: review shows v1 and v2 of Alice’s fix, then v1, v2, v3 of Bob’s feature, finally Alice’s fix rebased onto Bob’s. public just shows the final public version of each changeset] +

+
+
+
+
+

+ Getting into trouble with shared mutable history +

+

+ Mercurial with evolve is a powerful tool, and using powerful tools can have consequences. (You can cut yourself badly with a sharp knife, but every competent chef keeps several around. Ever try to chop onions with a spoon?) +

+

+ In the user guide, we saw examples of unstbale changesets, which are the most common type of troubled changeset. (Recall that a non-obsolete changeset with obsolete ancestors is an orphan.) +

+

+ Two other types of troubles can happen: divergent and bumped changesets. Both are more likely with shared mutable history, especially mutable history shared by multiple developers. +

+
+

+ Setting up +

+

+ For these examples, we’re going to use a slightly different workflow: as before, Alice and Bob share a public repository. But this time there is no review repository. Instead, Alice and Bob put on their cowboy hats, throw good practice to the wind, and pull directly from each other’s working repositories. +

+

+ So we throw away everything except public and reclone: +

+
+
$ rm -rf review alice bob
+$ hg clone public alice
+updating to branch default
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+$ hg clone public bob
+updating to branch default
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+

+ Once again we have to configure their repositories: enable evolve and (since Alice and Bob will be pulling directly from each other) make their repositories non-publishing. Edit Alice’s configuration: +

+
+
$ hg -R alice config --edit --local
+
+
+

+ and add +

+
+
[extensions]
+rebase =
+evolve =
+
+[phases]
+publish = false
+
+
+

+ Then edit Bob’s repository configuration: +

+
+
$ hg -R bob config --edit --local
+
+
+

+ and add the same text. +

+
+
+

+ Example 6: Divergent changesets +

+

+ When an obsolete changeset has two successors, those successors are divergent. One way to get into such a situation is by failing to communicate with your teammates. Let’s see how that might happen. +

+

+ First, we’ll have Bob commit a bug fix that could still be improved: +

+
+
$ cd bob
+$ echo 'pretty good fix' >> file1
+$ hg commit -u bob -m 'fix bug 24 (v1)'                   # rev 4:2fe6
+
+
+

+ Since Alice and Bob are now in cowboy mode, Alice pulls Bob’s draft changeset and amends it herself. +

+
+
$ cd ../alice
+$ hg pull -u ../bob
+[...]
+added 1 changesets with 1 changes to 1 files
+$ echo 'better fix (alice)' >> file1
+$ hg amend -u alice -m 'fix bug 24 (v2 by alice)'
+
+
+

+ But Bob has no idea that Alice just did this. (See how important good communication is?) So he implements a better fix of his own: +

+
+
$ cd ../bob
+$ echo 'better fix (bob)' >> file1
+$ hg amend -u bob -m 'fix bug 24 (v2 by bob)'             # rev 6:a360
+
+
+

+ At this point, the divergence exists, but only in theory: Bob’s original changeset, 4:2fe6, is obsolete and has two successors. But those successors are in different repositories, so the trouble is not visible to anyone yet. It will be as soon as Bob pulls from Alice’s repository (or vice-versa). +

+
+
$ hg pull ../alice
+[...]
+added 1 changesets with 1 changes to 2 files (+1 heads)
+(run 'hg heads' to see heads, 'hg merge' to merge)
+2 new divergent changesets
+
+
+

+ Figure 9 shows the situation in Bob’s repository. +

+
+

+ [figure SG09: Bob’s repo with 2 heads for the 2 divergent changesets, 6:a360 and 7:e3f9; wc is at 6:a360; both are successors of obsolete 4:2fe6, hence divergence] +

+
+

+ Now we need to get out of trouble. As usual, the answer is to evolve history. +

+
+
$ HGMERGE=internal:other hg evolve
+merge:[6] fix bug 24 (v2 by bob)
+with: [7] fix bug 24 (v2 by alice)
+base: [4] fix bug 24 (v1)
+0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+
+
+

+ Figure 10 shows how Bob’s repository looks now. +

+
+

+ [figure SG10: only one visible head, 9:5ad6, successor to hidden 6:a360 and 7:e3f9] +

+
+

+ We carefully dodged a merge conflict by specifying a merge tool (internal:other) that will take Alice’s changes over Bob’s. (You might wonder why Bob wouldn’t prefer his own changes by using internal:local. He’s avoiding a bug in evolve that occurs when evolving divergent changesets using internal:local.) +

+

+ # XXX this link does not work .. bug: https://bitbucket.org/marmoute/mutable-history/issue/48/ +

+

+ ** STOP HERE: WORK IN PROGRESS ** +

+
+
+

+ Phase-divergence: when a rewritten changeset is made public +

+

+ If Alice and Bob are collaborating on some mutable changesets, it’s possible to get into a situation where an otherwise worthwhile changeset cannot be pushed to the public repository; it is phase-divergent with another changeset that was made public first. Let’s demonstrate one way this could happen. +

+

+ It starts with Alice committing a bug fix. Right now, we don’t yet know if this bug fix is good enough to push to the public repository, but it’s good enough for Alice to commit. +

+
+
$ cd alice
+$ echo 'fix' > file2
+$ hg commit -A -m 'fix bug 15'
+adding file2
+
+
+

+ Now Bob has a bad idea: he decides to pull whatever Alice is working on and tweak her bug fix to his taste: +

+
+
$ cd ../bob
+$ hg pull -u ../alice
+[...]
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+$ echo 'Fix.' > file2
+$ hg amend -A -m 'fix bug 15 (amended)'
+
+
+

+ (Note the lack of communication between Alice and Bob. Failing to communicate with your colleagues is a good way to get into trouble. Nevertheless, evolve can usually sort things out, as we will see.) +

+
+

+ [figure SG06: Bob’s repo with one amendment] +

+
+

+ After some testing, Alice realizes her bug fix is just fine as it is: no need for further polishing and amending, this changeset is ready to publish. +

+
+
$ cd ../alice
+$ hg push
+[...]
+added 1 changesets with 1 changes to 1 files
+
+
+

+ This introduces a contradiction: in Bob’s repository, changeset 2:e011 (his copy of Alice’s fix) is obsolete, since Bob amended it. But in Alice’s repository (and the public repository), that changeset is public: it is immutable, carved in stone for all eternity. No changeset can be both obsolete and public, so Bob is in for a surprise the next time he pulls from public: +

+
+
$ cd ../bob
+$ hg pull -q -u
+1 new phase-divergent changesets
+
+
+

+ Figure 7 shows what just happened to Bob’s repository: changeset 2:e011 is now public, so it can’t be obsolete. When that changeset was obsolete, it made perfect sense for it to have a successor, namely Bob’s amendment of Alice’s fix (changeset 4:fe88). But it’s illogical for a public changeset to have a successor, so 4:fe88 is troubled: it has become bumped. +

+
+

+ [figure SG07: 2:e011 now public not obsolete, 4:fe88 now bumped] +

+
+

+ As usual when there’s trouble in your repository, the solution is to evolve it: +

+ +

+ Figure 8 illustrates Bob’s repository after evolving away the bumped changeset. Ignoring the obsolete changesets, Bob now has a nice, clean, simple history. His amendment of Alice’s bug fix lives on, as changeset 5:227d—albeit with a software-generated commit message. (Bob should probably amend that changeset to improve the commit message.) But the important thing is that his repository no longer has any troubled changesets, thanks to evolve. +

+
+

+ [figure SG08: 5:227d is new, formerly bumped changeset 4:fe88 now hidden] +

+
+
+
+
+

+ Conclusion +

+

+ Mutable history is a powerful tool. Like a sharp knife, an experienced user can do wonderful things with it, much more wonderful than with a dull knife (never mind a rusty spoon). At the same time, an inattentive or careless user can do harm to himself or others. Mercurial with evolve goes to great lengths to limit the harm you can do by trying to handle all possible types of “troubled” changesets. Nevertheless, having a first-aid kit nearby does not mean you should stop being careful with sharp knives. +

+

+ Mutable history shared across multiple repositories by a single developer is a natural extension of this model. Once you are used to using a single sharp knife on its own, it’s pretty straightforward to chop onions and mushrooms using the same knife, or to alternate between two chopping boards with different knives. +

+

+ Mutable history shared by multiple developers is a scary place to go. Imagine a professional kitchen full of expert chefs tossing their favourite knives back and forth, with the occasional axe or chainsaw thrown in to spice things up. If you’re confident that you and your colleagues can do it without losing a limb, go for it. But be sure to practice a lot first before you rely on it! +

+
+
\ No newline at end of file diff --git a/test/test-pages/mercurial/source.html b/test/test-pages/mercurial/source.html new file mode 100644 index 0000000..e8dc6f6 --- /dev/null +++ b/test/test-pages/mercurial/source.html @@ -0,0 +1,979 @@ + + + + + + Evolve: Shared Mutable History — evolve extension for Mercurial + + + + + + + + + + + + + +
+

+ evolve extension for Mercurial +

+

+ Evolve: Shared Mutable History +

+
+
+

+ «  Evolve: User Guide   ::   Contents   ::   Evolve: Concepts  » +

+
+
+
+

+ Evolve: Shared Mutable History +

+ +

+ Once you have mastered the art of mutable history in a single repository (see the user guide), you can move up to the next level: shared mutable history. evolve lets you push and pull draft changesets between repositories along with their obsolescence markers. This opens up a number of interesting possibilities. +

+

+ The simplest scenario is a single developer working across two computers. Say you’re working on code that must be tested on a remote test server, probably in a rack somewhere, only accessible by SSH, and running an “enterprise-grade” (out-of-date) OS. But you probably prefer to write code locally: everything is setup the way you like it, and you can use your preferred editor, IDE, merge/diff tools, etc. +

+

+ Traditionally, your options are limited: either +

+
+
+
    +
  • (ab)use your source control system by committing half-working code in order to get it onto the remote test server, or +
  • +
  • go behind source control’s back by using rsync (or similar) to transfer your code back-and-forth until it is ready to commit +
  • +
+
+
+

+ The former is less bad with distributed version control systems like Mercurial, but it’s still far from ideal. (One important version control “best practice” is that every commit should make things just a little bit better, i.e. you should never commit code that is worse than what came before.) The latter, avoiding version control entirely, means that you’re walking a tightrope without a safety net. One accidental rsync in the wrong direction could destroy hours of work. +

+

+ Using Mercurial with evolve to share mutable history solves these problems. As with single-repository evolve, you can commit whenever the code is demonstrably better, even if all the tests aren’t passing yet—just hg amend when they are. And you can transfer those half-baked changesets between repositories to try things out on your test server before anything is carved in stone. +

+

+ A less common scenario is multiple developers sharing mutable history, typically for code review. We’ll cover this scenario later. First, we will cover single-user sharing. +

+
+

+ Sharing with a single developer +

+
+

+ Publishing and non-publishing repositories +

+

+ The key to shared mutable history is to keep your changesets in draft phase as you pass them around. Recall that by default, hg push promotes changesets from draft to public, and public changesets are immutable. You can change this behaviour by reconfiguring the remote repository so that it is non-publishing. (Short version: set phases.publish to false. Long version follows.) +

+
+
+

+ Setting up +

+

+ We’ll work through an example with three local repositories, although in the real world they’d most likely be on three different computers. First, the public repository is where tested, polished changesets live, and it is where you synchronize with the rest of your team. +

+
+
+
$ hg init public
+
+
+
+

+ We’ll need two clones where work gets done, test-repo and dev-repo: +

+
+
+
$ hg clone public test-repo
+updating to branch default
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+$ hg clone test-repo dev-repo
+updating to branch default
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+
+

+ dev-repo is your local machine, with GUI merge tools and IDEs and everything configured just the way you like it. test-repo is the test server in a rack somewhere behind SSH. So for the most part, we’ll develop in dev-repo, push to test-repo, test and polish there, and push to public. +

+

+ The key to shared mutable history is to make the target repository, in this case test-repo, non-publishing. And, of course, we have to enable the evolve extension in both test-repo and dev-repo. +

+

+ First, edit the configuration for test-repo: +

+
+
+
$ hg -R test-repo config --edit --local
+
+
+
+

+ and add +

+
+
+
[phases]
+publish = false
+
+[extensions]
+evolve =
+
+
+
+

+ Then edit the configuration for dev-repo: +

+
+
+
$ hg -R dev-repo config --edit --local
+
+
+
+

+ and add +

+
+
+
[extensions]
+evolve =
+
+
+
+

+ Keep in mind that in real life, these repositories would probably be on separate computers, so you’d have to login to each one to configure each repository. +

+

+ To start things off, let’s make one public, immutable changeset: +

+
+
+
$ cd test-repo
+$ echo 'my new project' > file1
+$ hg add file1
+$ hg commit -m 'create new project'
+$ hg push
+[...]
+added 1 changesets with 1 changes to 1 files
+
+
+
+

+ and pull that into the development repository: +

+
+
+
$ cd ../dev-repo
+$ hg pull -u
+[...]
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+
+
+
+

+ Example 1: Amend a shared changeset +

+

+ Everything you learned in the user guide applies to work done in dev-repo. You can commit, amend, uncommit, evolve, and so forth just as before. +

+

+ Things get different when you push changesets to test-repo. Or rather, things stay the same, which is different: because we configured test-repo to be non-publishing, draft changesets stay draft when we push them to test-repo. Importantly, they’re also draft (mutable) in test-repo. +

+

+ Let’s commit a preliminary change and push it to test-repo for testing. +

+
+
+
$ echo 'fix fix fix' > file1
+$ hg commit -m 'prelim change'
+$ hg push ../test-repo
+
+
+
+

+ At this point, dev-repo and test-repo have the same changesets in the same phases: +

+
+
+ [figure SG01: rev 0:0dc9 public, rev 1:f649 draft, same on both repos] +
+
+

+ (You may notice a change in notation from the user guide: now changesets are labelled with their revision number and the first four digits of the 40-digit hexadecimal changeset ID. Mercurial revision numbers are never stable when working across repositories, especially when obsolescence is involved. We’ll see why shortly.) +

+

+ Now let’s switch to test-repo to test our change: +

+
+
+
$ cd ../test-repo
+$ hg update
+
+
+
+

+ Don’t forget to hg update! Pushing only adds changesets to a remote repository; it does not update the working directory (unless you have a hook that updates for you). +

+

+ Now let’s imagine the tests failed because we didn’t use proper punctuation and capitalization (oops). Let’s amend our preliminary fix (and fix the lame commit message while we’re at it): +

+
+
+
$ echo 'Fix fix fix.' > file1
+$ hg amend -m 'fix bug 37'
+
+
+
+

+ Now we’re in a funny intermediate state (figure 2): revision 1:f649 is obsolete in test-repo, having been replaced by revision 3:60ff (revision 2:2a03 is another one of those temporary amend commits that we saw in the user guide)—but dev-repo knows nothing of these recent developments. +

+
+
+ [figure SG02: test-repo has rev 0:0dc9 public, rev 1:f649, 2:2a03 obsolete, rev 3:60ff draft; dev-repo same as in SG01] +
+
+

+ Let’s resynchronize: +

+
+
+
$ cd ../dev-repo
+$ hg pull -u
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+
+

+ As seen in figure 3, this transfers the new changeset and the obsolescence marker for revision 1. However, it does not transfer the temporary amend commit, because it is hidden. Push and pull transfer obsolescence markers between repositories, but they do not transfer hidden changesets. +

+
+
+ [figure SG03: dev-repo grows new rev 2:60ff, marks 1:f649 obsolete] +
+
+

+ Because of this deliberately incomplete synchronization, revision numbers in test-repo and dev-repo are no longer consistent. We must use changeset IDs. +

+
+
+

+ Example 2: Amend again, locally +

+

+ This process can repeat. Perhaps you figure out a more elegant fix to the bug, and want to mutate history so nobody ever knows you had a less-than-perfect idea. We’ll implement it locally in dev-repo and push to test-repo: +

+
+
+
$ echo 'Fix, fix, and fix.' > file1
+$ hg amend
+$ hg push
+
+
+
+

+ This time around, the temporary amend commit is in dev-repo, and it is not transferred to test-repo—the same as before, just in the opposite direction. Figure 4 shows the two repositories after amending in dev-repo and pushing to test-repo. +

+
+
+ [figure SG04: each repo has one temporary amend commit, but they’re different in each one] +
+
+

+ Let’s hop over to test-repo to test the more elegant fix: +

+
+
+
$ cd ../test-repo
+$ hg update
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+
+

+ This time, all the tests pass, so no further amending is required. This bug fix is finished, so we push it to the public repository: +

+
+
+
$ hg push
+[...]
+added 1 changesets with 1 changes to 1 files
+
+
+
+

+ Note that only one changeset—the final version, after two amendments—was actually pushed. Again, Mercurial doesn’t transfer hidden changesets on push and pull. +

+

+ So the picture in public is much simpler than in either dev-repo or test-repo. Neither of our missteps nor our amendments are publicly visible, just the final, beautifully polished changeset: +

+
+
+ [figure SG05: public repo with rev 0:0dc9, 1:de61, both public] +
+
+

+ There is one important step left to do. Because we pushed from test-repo to public, the pushed changeset is in public phase in those two repositories. But dev-repo has been out-of-the-loop; changeset de61 is still draft there. If we’re not careful, we might mutate history in dev-repo, obsoleting a changeset that is already public. Let’s avoid that situation for now by pushing up to dev-repo: +

+
+
+
$ hg push ../dev-repo
+pushing to ../dev-repo
+searching for changes
+no changes found
+
+
+
+

+ Even though no changesets were pushed, Mercurial still pushed obsolescence markers and phase changes to dev-repo. +

+

+ A final note: since this fix is now public, it is immutable. It’s no longer possible to amend it: +

+
+
+
$ hg amend -m 'fix bug 37'
+abort: cannot amend public changesets
+
+
+
+

+ This is, after all, the whole point of Mercurial’s phases: to prevent rewriting history that has already been published. +

+
+
+
+

+ Sharing with multiple developers: code review +

+

+ Now that you know how to share your own mutable history across multiple computers, you might be wondering if it makes sense to share mutable history with others. It does, but you have to be careful, stay alert, and communicate with your peers. +

+

+ Code review is a good use case for sharing mutable history across multiple developers: Alice commits a draft changeset, submits it for review, and amends her changeset until her reviewer is satisfied. Meanwhile, Bob is also committing draft changesets for review, amending until his reviewer is satisfied. Once a particular changeset passes review, the respective author (Alice or Bob) pushes it to the public (publishing) repository. +

+

+ Incidentally, the reviewers here can be anyone: maybe Bob and Alice review each other’s work; maybe the same third party reviews both; or maybe they pick different experts to review their work on different parts of a large codebase. Similarly, it doesn’t matter if reviews are conducted in person, by email, or by carrier pigeon. Code review is outside of the scope of Mercurial, so all we’re looking at here is the mechanics of committing, amending, pushing, and pulling. +

+
+

+ Setting up +

+

+ To demonstrate, let’s start with the public repository as we left it in the last example, with two immutable changesets (figure 5 above). We’ll clone a review repository from it, and then Alice and Bob will both clone from review. +

+
+
+
$ hg clone public review
+updating to branch default
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+$ hg clone review alice
+updating to branch default
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+$ hg clone review bob
+updating to branch default
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+
+

+ We need to configure Alice’s and Bob’s working repositories to enable evolve. First, edit Alice’s configuration with +

+
+
+
$ hg -R alice config --edit --local
+
+
+
+

+ and add +

+
+
+
[extensions]
+evolve =
+
+
+
+

+ Then edit Bob’s repository configuration: +

+
+
+
$ hg -R bob config --edit --local
+
+
+
+

+ and add the same text. +

+
+
+

+ Example 3: Alice commits and amends a draft fix +

+

+ We’ll follow Alice working on a bug fix. We’re going to use bookmarks to make it easier to understand multiple branch heads in the review repository, so Alice starts off by creating a bookmark and committing her first attempt at a fix: +

+
+
+
$ hg bookmark bug15
+$ echo 'fix' > file2
+$ hg commit -A -u alice -m 'fix bug 15 (v1)'
+adding file2
+
+
+
+

+ Note the unorthodox “(v1)” in the commit message. We’re just using that to make this tutorial easier to follow; it’s not something we’d recommend in real life. +

+

+ Of course Alice wouldn’t commit unless her fix worked to her satisfaction, so it must be time to solicit a code review. She does this by pushing to the review repository: +

+
+
+
$ hg push -B bug15
+[...]
+added 1 changesets with 1 changes to 1 files
+exporting bookmark bug15
+
+
+
+

+ (The use of -B is important to ensure that we only push the bookmarked head, and that the bookmark itself is pushed. See this guide to bookmarks, especially the Sharing Bookmarks section, if you’re not familiar with bookmarks.) +

+

+ Some time passes, and Alice receives her code review. As a result, Alice revises her fix and submits it for a second review: +

+
+
+
$ echo 'Fix.' > file2
+$ hg amend -m 'fix bug 15 (v2)'
+$ hg push
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+updating bookmark bug15
+
+
+
+

+ Figure 6 shows the state of the review repository at this point. +

+
+
+ [figure SG06: rev 2:fn1e is Alice’s obsolete v1, rev 3:cbdf is her v2; both children of rev 1:de61] +
+
+

+ After a busy morning of bug fixing, Alice stops for lunch. Let’s see what Bob has been up to. +

+
+
+

+ Example 4: Bob implements and publishes a new feature +

+

+ Meanwhile, Bob has been working on a new feature. Like Alice, he’ll use a bookmark to track his work, and he’ll push that bookmark to the review repository, so that reviewers know which changesets to review. +

+
+
+
$ cd ../bob
+$ echo 'stuff' > file1
+$ hg bookmark featureX
+$ hg commit -u bob -m 'implement feature X (v1)'          # rev 4:1636
+$ hg push -B featureX
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+exporting bookmark featureX
+
+
+
+

+ When Bob receives his code review, he improves his implementation a bit, amends, and submits the resulting changeset for review: +

+
+
+
$ echo 'do stuff' > file1
+$ hg amend -m 'implement feature X (v2)'                  # rev 5:0eb7
+$ hg push
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+updating bookmark featureX
+
+
+
+

+ Unfortunately, that still doesn’t pass muster. Bob’s reviewer insists on proper capitalization and punctuation. +

+
+
+
$ echo 'Do stuff.' > file1
+$ hg amend -m 'implement feature X (v3)'                  # rev 6:540b
+
+
+
+

+ On the bright side, the second review said, “Go ahead and publish once you fix that.” So Bob immediately publishes his third attempt: +

+
+
+
$ hg push ../public
+[...]
+added 1 changesets with 1 changes to 1 files
+
+
+
+

+ It’s not enough just to update public, though! Other people also use the review repository, and right now it doesn’t have Bob’s latest amendment (“v3”, revision 6:540b), nor does it know that the precursor of that changeset (“v2”, revision 5:0eb7) is obsolete. Thus, Bob pushes to review as well: +

+
+
+
$ hg push ../review
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+updating bookmark featureX
+
+
+
+

+ Figure 7 shows the result of Bob’s work in both review and public. +

+
+
+ [figure SG07: review includes Alice’s draft work on bug 15, as well as Bob’s v1, v2, and v3 changes for feature X: v1 and v2 obsolete, v3 public. public contains only the final, public implementation of feature X] +
+
+

+ Incidentally, it’s important that Bob push to public before review. If he pushed to review first, then revision 6:540b would still be in draft phase in review, but it would be public in both Bob’s local repository and the public repository. That could lead to confusion at some point, which is easily avoided by pushing first to public. +

+
+
+

+ Example 5: Alice integrates and publishes +

+

+ Finally, Alice gets back from lunch and sees that the carrier pigeon with her second review has arrived (or maybe it’s in her email inbox). Alice’s reviewer approved her amended changeset, so she pushes it to public: +

+
+
+
$ hg push ../public
+[...]
+remote has heads on branch 'default' that are not known locally: 540ba8f317e6
+abort: push creates new remote head cbdfbd5a5db2!
+(pull and merge or see "hg help push" for details about pushing new heads)
+
+
+
+

+ Oops! Bob has won the race to push first to public. So Alice needs to integrate with Bob: let’s pull his changeset(s) and see what the branch heads are. +

+
+
+
$ hg pull ../public
+[...]
+added 1 changesets with 1 changes to 1 files (+1 heads)
+(run 'hg heads' to see heads, 'hg merge' to merge)
+$ hg log -G -q -r 'head()' --template '{rev}:{node|short}  ({author})\n'
+o  5:540ba8f317e6  (bob)
+|
+| @  4:cbdfbd5a5db2  (alice)
+|/
+
+
+
+

+ We’ll assume Alice and Bob are perfectly comfortable with rebasing changesets. (After all, they’re already using mutable history in the form of amend.) So Alice rebases her changeset on top of Bob’s and publishes the result: +

+
+
+
$ hg rebase -d 5
+$ hg push ../public
+[...]
+added 1 changesets with 1 changes to 1 files
+$ hg push ../review
+[...]
+added 1 changesets with 0 changes to 0 files
+updating bookmark bug15
+
+
+
+

+ The result, in both review and public repositories, is shown in figure 8. +

+
+
+ [figure SG08: review shows v1 and v2 of Alice’s fix, then v1, v2, v3 of Bob’s feature, finally Alice’s fix rebased onto Bob’s. public just shows the final public version of each changeset] +
+
+
+
+
+

+ Getting into trouble with shared mutable history +

+

+ Mercurial with evolve is a powerful tool, and using powerful tools can have consequences. (You can cut yourself badly with a sharp knife, but every competent chef keeps several around. Ever try to chop onions with a spoon?) +

+

+ In the user guide, we saw examples of unstbale changesets, which are the most common type of troubled changeset. (Recall that a non-obsolete changeset with obsolete ancestors is an orphan.) +

+

+ Two other types of troubles can happen: divergent and bumped changesets. Both are more likely with shared mutable history, especially mutable history shared by multiple developers. +

+
+

+ Setting up +

+

+ For these examples, we’re going to use a slightly different workflow: as before, Alice and Bob share a public repository. But this time there is no review repository. Instead, Alice and Bob put on their cowboy hats, throw good practice to the wind, and pull directly from each other’s working repositories. +

+

+ So we throw away everything except public and reclone: +

+
+
+
$ rm -rf review alice bob
+$ hg clone public alice
+updating to branch default
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+$ hg clone public bob
+updating to branch default
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+
+
+

+ Once again we have to configure their repositories: enable evolve and (since Alice and Bob will be pulling directly from each other) make their repositories non-publishing. Edit Alice’s configuration: +

+
+
+
$ hg -R alice config --edit --local
+
+
+
+

+ and add +

+
+
+
[extensions]
+rebase =
+evolve =
+
+[phases]
+publish = false
+
+
+
+

+ Then edit Bob’s repository configuration: +

+
+
+
$ hg -R bob config --edit --local
+
+
+
+

+ and add the same text. +

+
+
+

+ Example 6: Divergent changesets +

+

+ When an obsolete changeset has two successors, those successors are divergent. One way to get into such a situation is by failing to communicate with your teammates. Let’s see how that might happen. +

+

+ First, we’ll have Bob commit a bug fix that could still be improved: +

+
+
+
$ cd bob
+$ echo 'pretty good fix' >> file1
+$ hg commit -u bob -m 'fix bug 24 (v1)'                   # rev 4:2fe6
+
+
+
+

+ Since Alice and Bob are now in cowboy mode, Alice pulls Bob’s draft changeset and amends it herself. +

+
+
+
$ cd ../alice
+$ hg pull -u ../bob
+[...]
+added 1 changesets with 1 changes to 1 files
+$ echo 'better fix (alice)' >> file1
+$ hg amend -u alice -m 'fix bug 24 (v2 by alice)'
+
+
+
+

+ But Bob has no idea that Alice just did this. (See how important good communication is?) So he implements a better fix of his own: +

+
+
+
$ cd ../bob
+$ echo 'better fix (bob)' >> file1
+$ hg amend -u bob -m 'fix bug 24 (v2 by bob)'             # rev 6:a360
+
+
+
+

+ At this point, the divergence exists, but only in theory: Bob’s original changeset, 4:2fe6, is obsolete and has two successors. But those successors are in different repositories, so the trouble is not visible to anyone yet. It will be as soon as Bob pulls from Alice’s repository (or vice-versa). +

+
+
+
$ hg pull ../alice
+[...]
+added 1 changesets with 1 changes to 2 files (+1 heads)
+(run 'hg heads' to see heads, 'hg merge' to merge)
+2 new divergent changesets
+
+
+
+

+ Figure 9 shows the situation in Bob’s repository. +

+
+
+ [figure SG09: Bob’s repo with 2 heads for the 2 divergent changesets, 6:a360 and 7:e3f9; wc is at 6:a360; both are successors of obsolete 4:2fe6, hence divergence] +
+
+

+ Now we need to get out of trouble. As usual, the answer is to evolve history. +

+
+
+
$ HGMERGE=internal:other hg evolve
+merge:[6] fix bug 24 (v2 by bob)
+with: [7] fix bug 24 (v2 by alice)
+base: [4] fix bug 24 (v1)
+0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+
+
+
+

+ Figure 10 shows how Bob’s repository looks now. +

+
+
+ [figure SG10: only one visible head, 9:5ad6, successor to hidden 6:a360 and 7:e3f9] +
+
+

+ We carefully dodged a merge conflict by specifying a merge tool (internal:other) that will take Alice’s changes over Bob’s. (You might wonder why Bob wouldn’t prefer his own changes by using internal:local. He’s avoiding a bug in evolve that occurs when evolving divergent changesets using internal:local.) +

+

+ # XXX this link does not work .. bug: https://bitbucket.org/marmoute/mutable-history/issue/48/ +

+

+ ** STOP HERE: WORK IN PROGRESS ** +

+
+
+

+ Phase-divergence: when a rewritten changeset is made public +

+

+ If Alice and Bob are collaborating on some mutable changesets, it’s possible to get into a situation where an otherwise worthwhile changeset cannot be pushed to the public repository; it is phase-divergent with another changeset that was made public first. Let’s demonstrate one way this could happen. +

+

+ It starts with Alice committing a bug fix. Right now, we don’t yet know if this bug fix is good enough to push to the public repository, but it’s good enough for Alice to commit. +

+
+
+
$ cd alice
+$ echo 'fix' > file2
+$ hg commit -A -m 'fix bug 15'
+adding file2
+
+
+
+

+ Now Bob has a bad idea: he decides to pull whatever Alice is working on and tweak her bug fix to his taste: +

+
+
+
$ cd ../bob
+$ hg pull -u ../alice
+[...]
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+$ echo 'Fix.' > file2
+$ hg amend -A -m 'fix bug 15 (amended)'
+
+
+
+

+ (Note the lack of communication between Alice and Bob. Failing to communicate with your colleagues is a good way to get into trouble. Nevertheless, evolve can usually sort things out, as we will see.) +

+
+
+ [figure SG06: Bob’s repo with one amendment] +
+
+

+ After some testing, Alice realizes her bug fix is just fine as it is: no need for further polishing and amending, this changeset is ready to publish. +

+
+
+
$ cd ../alice
+$ hg push
+[...]
+added 1 changesets with 1 changes to 1 files
+
+
+
+

+ This introduces a contradiction: in Bob’s repository, changeset 2:e011 (his copy of Alice’s fix) is obsolete, since Bob amended it. But in Alice’s repository (and the public repository), that changeset is public: it is immutable, carved in stone for all eternity. No changeset can be both obsolete and public, so Bob is in for a surprise the next time he pulls from public: +

+
+
+
$ cd ../bob
+$ hg pull -q -u
+1 new phase-divergent changesets
+
+
+
+

+ Figure 7 shows what just happened to Bob’s repository: changeset 2:e011 is now public, so it can’t be obsolete. When that changeset was obsolete, it made perfect sense for it to have a successor, namely Bob’s amendment of Alice’s fix (changeset 4:fe88). But it’s illogical for a public changeset to have a successor, so 4:fe88 is troubled: it has become bumped. +

+
+
+ [figure SG07: 2:e011 now public not obsolete, 4:fe88 now bumped] +
+
+

+ As usual when there’s trouble in your repository, the solution is to evolve it: +

+
+
+
$ hg evolve --all
+
+
+
+

+ Figure 8 illustrates Bob’s repository after evolving away the bumped changeset. Ignoring the obsolete changesets, Bob now has a nice, clean, simple history. His amendment of Alice’s bug fix lives on, as changeset 5:227d—albeit with a software-generated commit message. (Bob should probably amend that changeset to improve the commit message.) But the important thing is that his repository no longer has any troubled changesets, thanks to evolve. +

+
+
+ [figure SG08: 5:227d is new, formerly bumped changeset 4:fe88 now hidden] +
+
+
+
+
+

+ Conclusion +

+

+ Mutable history is a powerful tool. Like a sharp knife, an experienced user can do wonderful things with it, much more wonderful than with a dull knife (never mind a rusty spoon). At the same time, an inattentive or careless user can do harm to himself or others. Mercurial with evolve goes to great lengths to limit the harm you can do by trying to handle all possible types of “troubled” changesets. Nevertheless, having a first-aid kit nearby does not mean you should stop being careful with sharp knives. +

+

+ Mutable history shared across multiple repositories by a single developer is a natural extension of this model. Once you are used to using a single sharp knife on its own, it’s pretty straightforward to chop onions and mushrooms using the same knife, or to alternate between two chopping boards with different knives. +

+

+ Mutable history shared by multiple developers is a scary place to go. Imagine a professional kitchen full of expert chefs tossing their favourite knives back and forth, with the occasional axe or chainsaw thrown in to spice things up. If you’re confident that you and your colleagues can do it without losing a limb, go for it. But be sure to practice a lot first before you rely on it! +

+
+
+
+
+

+ «  Evolve: User Guide   ::   Contents   ::   Evolve: Concepts  » +

+
+ + + diff --git a/test/test-pages/nytimes-4/expected-images.json b/test/test-pages/nytimes-4/expected-images.json new file mode 100644 index 0000000..58b7788 --- /dev/null +++ b/test/test-pages/nytimes-4/expected-images.json @@ -0,0 +1,4 @@ +[ + "https:\/\/static01.nyt.com\/images\/2018\/09\/15\/business\/15DEBTS01\/15DEBTS01-facebookJumbo.jpg", + "https:\/\/static01.nyt.com\/images\/2018\/09\/15\/business\/15DEBTS01\/merlin_138209730_5f3b5746-4962-4207-a24f-aea644a8636f-articleLarge.jpg?quality=75&auto=webp&disable=upscale" +] \ No newline at end of file diff --git a/test/test-pages/nytimes-4/expected-metadata.json b/test/test-pages/nytimes-4/expected-metadata.json new file mode 100644 index 0000000..52b7f13 --- /dev/null +++ b/test/test-pages/nytimes-4/expected-metadata.json @@ -0,0 +1,8 @@ +{ + "Author": "By Nelson D. Schwartz", + "Direction": null, + "Excerpt": "Tax cuts, spending increases and higher interest rates could make it harder to respond to future recessions and deal with other needs.", + "Image": "https:\/\/static01.nyt.com\/images\/2018\/09\/15\/business\/15DEBTS01\/15DEBTS01-facebookJumbo.jpg", + "Title": "As Debt Rises, the Government Will Soon Spend More on Interest Than on the Military", + "SiteName": null +} \ No newline at end of file diff --git a/test/test-pages/nytimes-4/expected.html b/test/test-pages/nytimes-4/expected.html new file mode 100644 index 0000000..855eb55 --- /dev/null +++ b/test/test-pages/nytimes-4/expected.html @@ -0,0 +1,214 @@ +
+ +
+ + +

+ Tax cuts, spending increases and higher interest rates could make it harder to respond to future recessions and deal with other needs. +

+
+
+

Image +

+
+ Interest payments on the federal debt could surpass the Defense Department budget in 2023.CreditCreditJeon Heon-Kyun/EPA, via Shutterstock +
+
+
+ +
+
+
+

+ The federal government could soon pay more in interest on its debt than it spends on the military, Medicaid or children’s programs. +

+

+ The run-up in borrowing costs is a one-two punch brought on by the need to finance a fast-growing budget deficit, worsened by tax cuts and steadily rising interest rates that will make the debt more expensive. +

+

+ With less money coming in and more going toward interest, political leaders will find it harder to address pressing needs like fixing crumbling roads and bridges or to make emergency moves like pulling the economy out of future recessions. +

+

+ Within a decade, more than $900 billion in interest payments will be due annually, easily outpacing spending on myriad other programs. Already the fastest-growing major government expense, the cost of interest is on track to hit $390 billion next year, nearly 50 percent more than in 2017, according to the Congressional Budget Office. +

+
+
+

+ “It’s very much something to worry about,” said C. Eugene Steuerle, a fellow at the Urban Institute and a co-founder of the Urban-Brookings Tax Policy Center in Washington. “Everything else is getting squeezed.” +

+

+ Gradually rising interest rates would have made borrowing more expensive even without additional debt. But the tax cuts passed late last year have created a deeper hole, with the deficit increasing faster than expected. A budget bill approved in February that raised spending by $300 billion over two years will add to the financial pressure. +

+

+ The deficit is expected to total nearly $1 trillion next year — the first time it has been that big since 2012, when the economy was still struggling to recover from the financial crisis and interest rates were near zero. +

+
+ +
+

+ Deficit hawks have gone silent, even proposing changes that would exacerbate the deficit. House Republicans introduced legislation this month that would make the tax cuts permanent. +

+

+ “The issue has just disappeared,” said Senator Mark Warner, a Virginia Democrat. “There’s collective amnesia.” +

+
+
+

+ The combination, say economists, marks a journey into mostly uncharted financial territory. +

+

+ In the past, government borrowing expanded during recessions and waned in recoveries. That countercyclical policy has been a part of the standard Keynesian toolbox to combat downturns since the Great Depression. +

+

+ The deficit is soaring now as the economy booms, meaning the stimulus is pro-cyclical. The risk is that the government would have less room to maneuver if the economy slows. +

+
+ +
+

+ Aside from wartime or a deep downturn like the 1930s or 2008-9, “this sort of aggressive fiscal stimulus is unprecedented in U.S. history,” said Jeffrey Frankel, an economist at Harvard. +

+

+ Pouring gasoline on an already hot economy has resulted in faster growth — the economy expanded at an annualized rate of 4.2 percent in the second quarter. But Mr. Frankel warns that when the economy weakens, the government will find it more difficult to cut taxes or increase spending. +

+

+ Lawmakers might, in fact, feel compelled to cut spending as tax revenue falls, further depressing the economy. “There will eventually be another recession, and this increases the chances we will have to slam on the brakes when the car is already going too slowly,” Mr. Frankel said. +

+ +
+ +
+

+ Finding the money to pay investors who hold government debt will crimp other parts of the budget. In a decade, interest on the debt will eat up 13 percent of government spending, up from 6.6 percent in 2017. +

+

+ “By 2020, we will spend more on interest than we do on kids, including education, food stamps and aid to families,” said Marc Goldwein, senior policy director at the Committee for a Responsible Federal Budget, a research and advocacy organization. +

+
+
+

+ Interest costs already dwarf spending on many popular programs. For example, grants to students from low-income families for college total roughly $30 billion — about one-tenth of what the government will pay in interest this year. Interest payments will overtake Medicaid in 2020 and the Department of Defense budget in 2023. +

+

+ What’s more, the heavy burden of interest payments could make it harder for the government to repair aging infrastructure or take on other big new projects. +

+

+ Mr. Trump has called for spending $1 trillion on infrastructure, but Congress has not taken up that idea. +

+
+
+

+ More about the federal debt and the economy +

+ +
+
+ +

+ Until recently, ultralow interest rates, set by the Federal Reserve to support the economy, allowed lawmakers to borrow without fretting too much about the cost of that debt. +

+

+ But as the economy has strengthened, the Fed has gradually raised rates, starting in December 2015. The central bank is expected to push rates up again on Wednesday, and more increases are in store. +

+

+ “When rates went down to record lows, it allowed the government to take on more debt without paying more interest,” Mr. Goldwein said. “That party is ending.” +

+
+ +
+

+ Since the beginning of the year, the yield on the 10-year Treasury note has risen by more than half a percentage point, to 3.1 percent. The Congressional Budget Office estimates that the yield will climb to 4.2 percent in 2021. Given that the total public debt of the United States stands at nearly $16 trillion, even a small uptick in rates can cost the government billions. +

+ +
+
+

+ There’s no guarantee that these forecasts will prove accurate. If the economy weakens, rates might fall or rise only slightly, reducing interest payments. But rates could also overshoot the budget office forecast. +

+

+ Some members of Congress want to set the stage for even more red ink. Republicans in the House want to make last year’s tax cuts permanent, instead of letting some of them expire at the end of 2025. That would reduce federal revenue by an additional $631 billion over 10 years, according to the Tax Policy Center. +

+ +
+ +
+

+ Deficit hawks have warned for years that a day of reckoning is coming, exposing the United States to the kind of economic crisis that overtook profligate borrowers in the past like Greece or Argentina. +

+

+ But most experts say that isn’t likely because the dollar is the world’s reserve currency. As a result, the United States still has plenty of borrowing capacity left because the Fed can print money with fewer consequences than other central banks. +

+

+ And interest rates plunged over the last decade, even as the government turned to the market for trillions each year after the recession. That’s because Treasury bonds are still the favored port of international investors in any economic storm. +

+

+ “We exported a financial crisis a decade ago, and the world responded by sending us money,” said William G. Gale, a senior fellow at the Brookings Institution. +

+

+ But that privileged position has allowed politicians in both parties to avoid politically painful steps like cutting spending or raising taxes. +

+
+
+

+ That doesn’t mean rapidly rising interest costs and a bigger deficit won’t eventually catch up with us. +

+

+ Charles Schultze, chairman of the Council of Economic Advisers in the Carter administration, once summed up the danger of deficits with a metaphor. “It’s not so much a question of the wolf at the door, but termites in the woodwork.” +

+ +

+ Rather than simply splitting along party lines, lawmakers’ attitudes toward the deficit also depend on which party is in power. Republicans pilloried the Obama administration for proposing a large stimulus in the depths of the recession in 2009 and complained about the deficit for years. +

+

+ In 2013, Senator Mitch McConnell of Kentucky called the debt and deficit “the transcendent issue of our era.” By 2017, as Senate majority leader, he quickly shepherded the tax cut through Congress. +

+

+ Senator James Lankford, an Oklahoma Republican who warned of the deficit’s dangers in the past, nevertheless played down that threat on the Senate floor as the tax billed neared passage. +

+

+ “I understand it’s a risk, but I think it’s an appropriate risk to be able to say let’s allow Americans to keep more of their own money to invest in this economy,” he said. +

+

+ He also claimed the tax cuts would pay for themselves even as the Congressional Budget Office estimated that they would add $250 billion to the deficit on average from 2019 to 2024. +

+
+
+

+ In an interview, Mr. Lankford insisted that the jury was still out on whether the tax cuts would generate additional revenue, citing the strong economic growth recently. +

+

+ While the Republican about-face has been much more striking, Democrats have adjusted their position, too. +

+

+ Mr. Warner, the Virginia Democrat, called last year’s tax bill “the worst piece of legislation we have passed since I arrived in the Senate.” In 2009, however, when Congress passed an $800 billion stimulus bill backed by the Obama administration, he called it “a responsible mix of tax cuts and investments that will create jobs.” +

+

+ The difference, Mr. Warner said, was that the economy was near the precipice then. +

+

+ “There was virtual unanimity among economists that we needed a stimulus,” he said. “But a $2 trillion tax cut at the end of a business cycle with borrowed money won’t end well.” +

+
+
+
+ +
+

+ Nelson D. Schwartz has covered economics since 2012. Previously, he wrote about Wall Street and banking, and also served as European economic correspondent in Paris. He joined The Times in 2007 as a feature writer for the Sunday Business section. @NelsonSchwartz +

+
+

+ A version of this article appears in print on

, on Page

A

1

of the New York edition

with the headline:

What May Soon Exceed Cost of U.S. Military? Interest on U.S. Debt . Order Reprints | Today’s Paper | Subscribe +

+ +
+ +
\ No newline at end of file diff --git a/test/test-pages/nytimes-4/source.html b/test/test-pages/nytimes-4/source.html new file mode 100644 index 0000000..81a2a68 --- /dev/null +++ b/test/test-pages/nytimes-4/source.html @@ -0,0 +1,3896 @@ + + + + + + As Debt Rises, the Government Will Soon Spend More on Interest Than on the Military - The New York Times + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+ +
Skip to contentSkip to site index +
+
+ Economy +
+
+ + +
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+ + + + +
+
+
+
+
+ +
+

+ As Debt Rises, the Government Will Soon Spend More on Interest Than on the Military +

+
+

+ Tax cuts, spending increases and higher interest rates could make it harder to respond to future recessions and deal with other needs. +

+
+
+
+
+
+ Image +
+
+ Interest payments on the federal debt could surpass the Defense Department budget in 2023.CreditCreditJeon Heon-Kyun/EPA, via Shutterstock +
+
+
+
+
+
+
+
+

+ By Nelson D. Schwartz +

+
+
+
    +
  • + +
  • +
  • +
    + +
    +
  • +
+
+
+
+
+
+
+

+ The federal government could soon pay more in interest on its debt than it spends on the military, Medicaid or children’s programs. +

+

+ The run-up in borrowing costs is a one-two punch brought on by the need to finance a fast-growing budget deficit, worsened by tax cuts and steadily rising interest rates that will make the debt more expensive. +

+

+ With less money coming in and more going toward interest, political leaders will find it harder to address pressing needs like fixing crumbling roads and bridges or to make emergency moves like pulling the economy out of future recessions. +

+

+ Within a decade, more than $900 billion in interest payments will be due annually, easily outpacing spending on myriad other programs. Already the fastest-growing major government expense, the cost of interest is on track to hit $390 billion next year, nearly 50 percent more than in 2017, according to the Congressional Budget Office. +

+
+ +
+
+
+

+ “It’s very much something to worry about,” said C. Eugene Steuerle, a fellow at the Urban Institute and a co-founder of the Urban-Brookings Tax Policy Center in Washington. “Everything else is getting squeezed.” +

+

+ Gradually rising interest rates would have made borrowing more expensive even without additional debt. But the tax cuts passed late last year have created a deeper hole, with the deficit increasing faster than expected. A budget bill approved in February that raised spending by $300 billion over two years will add to the financial pressure. +

+

+ The deficit is expected to total nearly $1 trillion next year — the first time it has been that big since 2012, when the economy was still struggling to recover from the financial crisis and interest rates were near zero. +

+
+ +
+
+
+
+

+ Annual interest payments on the national debt are expected to triple over the next decade, according to the Congressional Budget Office. +

+
+
+ + + + +
+ +
+ +
+

+ $915 +

+

+ billion +

+
+
+

+ Interest payments on the national debt +

+
+
+

+ $900 +

+
+
+

+ billion +

+
+
+

+ 600 +

+
+
+

+ 300 +

+
+
+

+ $263 +

+

+ billion +

+
+
+

+ 0 +

+
+
+

+ ’17 +

+
+
+

+ ’18 +

+
+
+

+ ’19 +

+
+
+

+ ’20 +

+
+
+

+ ’21 +

+
+
+

+ ’22 +

+
+
+

+ ’23 +

+
+
+

+ ’24 +

+
+
+

+ ’25 +

+
+
+

+ ’26 +

+
+
+

+ ’27 +

+
+
+

+ ’28 +

+
+
+
+ +
+

+ Interest payments on the national debt +

+
+
+

+ $900 +

+
+
+

+ billion +

+
+
+

+ $915 +

+

+ billion +

+
+
+

+ 600 +

+
+
+

+ 300 +

+
+
+

+ $263 +

+

+ billion +

+
+
+

+ 0 +

+
+
+

+ ’17 +

+
+
+

+ ’18 +

+
+
+

+ ’19 +

+
+
+

+ ’20 +

+
+
+

+ ’21 +

+
+
+

+ ’22 +

+
+
+

+ ’23 +

+
+
+

+ ’24 +

+
+
+

+ ’25 +

+
+
+

+ ’26 +

+
+
+

+ ’27 +

+
+
+

+ ’28 +

+
+
+
+ + +
+
+

+ By The New York Times | Source: Congressional Budget Office +

+
+
+
+
+
+

+ Deficit hawks have gone silent, even proposing changes that would exacerbate the deficit. House Republicans introduced legislation this month that would make the tax cuts permanent. +

+

+ “The issue has just disappeared,” said Senator Mark Warner, a Virginia Democrat. “There’s collective amnesia.” +

+
+ +
+
+
+

+ The combination, say economists, marks a journey into mostly uncharted financial territory. +

+

+ In the past, government borrowing expanded during recessions and waned in recoveries. That countercyclical policy has been a part of the standard Keynesian toolbox to combat downturns since the Great Depression. +

+

+ The deficit is soaring now as the economy booms, meaning the stimulus is pro-cyclical. The risk is that the government would have less room to maneuver if the economy slows. +

+
+ +
+
+
+
+

+ Debt as a percentage of gross domestic product tends to increase during recessions and fall during recoveries. But the debt is increasing now, even as the economy is growing, because of tax cuts and spending increases. +

+
+
+ + + +
+ +
+ +
+

+ Public debt as a share of gross domestic product +

+
+
+

+ 100 +

+
+
+

+ % +

+
+
+

+ RECESSIONS +

+
+
+

+ PROJECTED +

+
+
+

+ 80 +

+
+
+

+ 60 +

+
+
+

+ 40 +

+
+
+

+ 20 +

+
+
+

+ 0 +

+
+
+

+ ’78 +

+
+
+

+ ’80 +

+
+
+

+ ’90 +

+
+
+

+ ’00 +

+
+
+

+ ’10 +

+
+
+

+ ’20 +

+
+
+

+ ’28 +

+
+
+
+ +
+

+ Public debt as a share of gross domestic product +

+
+
+

+ 100 +

+
+
+

+ % +

+
+
+

+ 80 +

+
+
+

+ 60 +

+
+
+

+ 40 +

+
+
+

+ 20 +

+
+
+

+ RECESSIONS +

+
+
+

+ PROJECTED +

+
+
+

+ 0 +

+
+
+

+ ’80 +

+
+
+

+ ’90 +

+
+
+

+ ’00 +

+
+
+

+ ’10 +

+
+
+

+ ’20 +

+
+
+

+ ’28 +

+
+
+
+ + +
+
+

+ By The New York Times | Source: Congressional Budget Office +

+
+
+
+
+
+

+ Aside from wartime or a deep downturn like the 1930s or 2008-9, “this sort of aggressive fiscal stimulus is unprecedented in U.S. history,” said Jeffrey Frankel, an economist at Harvard. +

+

+ Pouring gasoline on an already hot economy has resulted in faster growth — the economy expanded at an annualized rate of 4.2 percent in the second quarter. But Mr. Frankel warns that when the economy weakens, the government will find it more difficult to cut taxes or increase spending. +

+

+ Lawmakers might, in fact, feel compelled to cut spending as tax revenue falls, further depressing the economy. “There will eventually be another recession, and this increases the chances we will have to slam on the brakes when the car is already going too slowly,” Mr. Frankel said. +

+ +
+ +
+
+
+
+

+ Interest payments will make up 13 percent of the federal budget a decade from now, surpassing spending on Medicaid and defense. +

+
+
+ + + + +
+ +
+ +
+

+ Expenditures as a share of overall budget +

+
+
+

+ 15 +

+
+
+

+ % +

+
+
+

+ Net interest +

+
+
+

+ 13.0% +

+
+
+

+ Defense +

+
+
+

+ 10 +

+
+
+

+ Medicaid +

+
+
+

+ 6.6% +

+
+
+

+ 5 +

+
+
+

+ 0 +

+
+
+

+ ’17 +

+
+
+

+ ’18 +

+
+
+

+ ’19 +

+
+
+

+ ’20 +

+
+
+

+ ’21 +

+
+
+

+ ’22 +

+
+
+

+ ’23 +

+
+
+

+ ’24 +

+
+
+

+ ’25 +

+
+
+

+ ’26 +

+
+
+

+ ’27 +

+
+
+

+ ’28 +

+
+
+
+ +
+

+ Expenditures as a share of overall budget +

+
+
+

+ 15 +

+
+
+

+ % +

+
+
+

+ Net interest +

+
+
+

+ 13.0% +

+
+
+

+ Defense +

+
+
+

+ 10 +

+
+
+

+ Medicaid +

+
+
+

+ 6.6% +

+
+
+

+ 5 +

+
+
+

+ 0 +

+
+
+

+ ’17 +

+
+
+

+ ’18 +

+
+
+

+ ’19 +

+
+
+

+ ’20 +

+
+
+

+ ’21 +

+
+
+

+ ’22 +

+
+
+

+ ’23 +

+
+
+

+ ’24 +

+
+
+

+ ’25 +

+
+
+

+ ’26 +

+
+
+

+ ’27 +

+
+
+

+ ’28 +

+
+
+
+ + +
+
+

+ By The New York Times | Source: Congressional Budget Office +

+
+
+
+
+
+

+ Finding the money to pay investors who hold government debt will crimp other parts of the budget. In a decade, interest on the debt will eat up 13 percent of government spending, up from 6.6 percent in 2017. +

+

+ “By 2020, we will spend more on interest than we do on kids, including education, food stamps and aid to families,” said Marc Goldwein, senior policy director at the Committee for a Responsible Federal Budget, a research and advocacy organization. +

+
+ +
+
+
+

+ Interest costs already dwarf spending on many popular programs. For example, grants to students from low-income families for college total roughly $30 billion — about one-tenth of what the government will pay in interest this year. Interest payments will overtake Medicaid in 2020 and the Department of Defense budget in 2023. +

+

+ What’s more, the heavy burden of interest payments could make it harder for the government to repair aging infrastructure or take on other big new projects. +

+

+ Mr. Trump has called for spending $1 trillion on infrastructure, but Congress has not taken up that idea. +

+
+ +
+ +
+
+ +

+ Until recently, ultralow interest rates, set by the Federal Reserve to support the economy, allowed lawmakers to borrow without fretting too much about the cost of that debt. +

+

+ But as the economy has strengthened, the Fed has gradually raised rates, starting in December 2015. The central bank is expected to push rates up again on Wednesday, and more increases are in store. +

+

+ “When rates went down to record lows, it allowed the government to take on more debt without paying more interest,” Mr. Goldwein said. “That party is ending.” +

+
+ +
+
+
+
+

+ After bottoming out below 2 percent, the yield on the 10-year Treasury note has climbed to over 3 percent recently. +

+
+
+ + + + +
+ +
+ +
+

+ Yield on 10-year Treasury note +

+
+
+

+ 8 +

+
+
+

+ % +

+
+
+

+ MONTHLY +

+
+
+

+ RECESSIONS +

+
+
+

+ 6 +

+
+
+

+ 4 +

+
+
+

+ 2 +

+
+
+

+ 0 +

+
+
+

+ ’98 +

+
+
+

+ ’00 +

+
+
+

+ ’02 +

+
+
+

+ ’04 +

+
+
+

+ ’06 +

+
+
+

+ ’08 +

+
+
+

+ ’10 +

+
+
+

+ ’12 +

+
+
+

+ ’14 +

+
+
+

+ ’16 +

+
+
+

+ ’18 +

+
+
+
+ +
+

+ Yield on 10-year Treasury note +

+
+
+

+ 8 +

+
+
+

+ % +

+
+
+

+ MONTHLY +

+
+
+

+ RECESSIONS +

+
+
+

+ 6 +

+
+
+

+ 4 +

+
+
+

+ 2 +

+
+
+

+ 0 +

+
+
+

+ ’98 +

+
+
+

+ ’02 +

+
+
+

+ ’06 +

+
+
+

+ ’10 +

+
+
+

+ ’14 +

+
+
+

+ ’18 +

+
+
+
+ + +
+
+

+ By The New York Times | Source: Reuters +

+
+
+
+
+
+

+ Since the beginning of the year, the yield on the 10-year Treasury note has risen by more than half a percentage point, to 3.1 percent. The Congressional Budget Office estimates that the yield will climb to 4.2 percent in 2021. Given that the total public debt of the United States stands at nearly $16 trillion, even a small uptick in rates can cost the government billions. +

+
+ +
+
+
+

+ There’s no guarantee that these forecasts will prove accurate. If the economy weakens, rates might fall or rise only slightly, reducing interest payments. But rates could also overshoot the budget office forecast. +

+

+ Some members of Congress want to set the stage for even more red ink. Republicans in the House want to make last year’s tax cuts permanent, instead of letting some of them expire at the end of 2025. That would reduce federal revenue by an additional $631 billion over 10 years, according to the Tax Policy Center. +

+ +
+ +
+
+
+
+

+ The dollar has strengthened even as government borrowing has increased. +

+
+
+ + + + +
+ +
+ +
+

+ U.S. Dollar index +

+
+
+

+ 140 +

+
+
+

+ MONTHLY +

+
+
+

+ RECESSIONS +

+
+
+

+ 120 +

+
+
+

+ 100 +

+
+
+

+ 80 +

+
+
+

+ 60 +

+
+
+

+ ’98 +

+
+
+

+ ’00 +

+
+
+

+ ’02 +

+
+
+

+ ’04 +

+
+
+

+ ’06 +

+
+
+

+ ’08 +

+
+
+

+ ’10 +

+
+
+

+ ’12 +

+
+
+

+ ’14 +

+
+
+

+ ’16 +

+
+
+

+ ’18 +

+
+
+
+ +
+

+ U.S. Dollar index +

+
+
+

+ 140 +

+
+
+

+ MONTHLY +

+
+
+

+ RECESSIONS +

+
+
+

+ 120 +

+
+
+

+ 100 +

+
+
+

+ 80 +

+
+
+

+ 60 +

+
+
+

+ ’98 +

+
+
+

+ ’02 +

+
+
+

+ ’06 +

+
+
+

+ ’10 +

+
+
+

+ ’14 +

+
+
+

+ ’18 +

+
+
+
+ + +
+
+

+ By The New York Times | Source: Reuters +

+
+
+
+
+
+

+ Deficit hawks have warned for years that a day of reckoning is coming, exposing the United States to the kind of economic crisis that overtook profligate borrowers in the past like Greece or Argentina. +

+

+ But most experts say that isn’t likely because the dollar is the world’s reserve currency. As a result, the United States still has plenty of borrowing capacity left because the Fed can print money with fewer consequences than other central banks. +

+

+ And interest rates plunged over the last decade, even as the government turned to the market for trillions each year after the recession. That’s because Treasury bonds are still the favored port of international investors in any economic storm. +

+

+ “We exported a financial crisis a decade ago, and the world responded by sending us money,” said William G. Gale, a senior fellow at the Brookings Institution. +

+

+ But that privileged position has allowed politicians in both parties to avoid politically painful steps like cutting spending or raising taxes. +

+
+ +
+
+
+

+ That doesn’t mean rapidly rising interest costs and a bigger deficit won’t eventually catch up with us. +

+

+ Charles Schultze, chairman of the Council of Economic Advisers in the Carter administration, once summed up the danger of deficits with a metaphor. “It’s not so much a question of the wolf at the door, but termites in the woodwork.” +

+ +

+ Rather than simply splitting along party lines, lawmakers’ attitudes toward the deficit also depend on which party is in power. Republicans pilloried the Obama administration for proposing a large stimulus in the depths of the recession in 2009 and complained about the deficit for years. +

+

+ In 2013, Senator Mitch McConnell of Kentucky called the debt and deficit “the transcendent issue of our era.” By 2017, as Senate majority leader, he quickly shepherded the tax cut through Congress. +

+

+ Senator James Lankford, an Oklahoma Republican who warned of the deficit’s dangers in the past, nevertheless played down that threat on the Senate floor as the tax billed neared passage. +

+

+ “I understand it’s a risk, but I think it’s an appropriate risk to be able to say let’s allow Americans to keep more of their own money to invest in this economy,” he said. +

+

+ He also claimed the tax cuts would pay for themselves even as the Congressional Budget Office estimated that they would add $250 billion to the deficit on average from 2019 to 2024. +

+
+ +
+
+
+

+ In an interview, Mr. Lankford insisted that the jury was still out on whether the tax cuts would generate additional revenue, citing the strong economic growth recently. +

+

+ While the Republican about-face has been much more striking, Democrats have adjusted their position, too. +

+

+ Mr. Warner, the Virginia Democrat, called last year’s tax bill “the worst piece of legislation we have passed since I arrived in the Senate.” In 2009, however, when Congress passed an $800 billion stimulus bill backed by the Obama administration, he called it “a responsible mix of tax cuts and investments that will create jobs.” +

+

+ The difference, Mr. Warner said, was that the economy was near the precipice then. +

+

+ “There was virtual unanimity among economists that we needed a stimulus,” he said. “But a $2 trillion tax cut at the end of a business cycle with borrowed money won’t end well.” +

+
+ +
+
+
+
+
+
+
+

+ Nelson D. Schwartz has covered economics since 2012. Previously, he wrote about Wall Street and banking, and also served as European economic correspondent in Paris. He joined The Times in 2007 as a feature writer for the Sunday Business section. @NelsonSchwartz +

+
+
+
+
+ A version of this article appears in print on , on Page A1 of the New York edition with the headline: What May Soon Exceed Cost of U.S. Military? Interest on U.S. Debt . Order Reprints | Today’s Paper | Subscribe +
+
+
+ +
+
+
+
+

+ Advertisement +

+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ + +
+
+
+ + + + + + +
+ +
+ + -- cgit v1.2.3