Commit Graph

1472 Commits

Author SHA1 Message Date
Jun Wu
6438f28e99 color: detect ConEmu and limit colors to 16
Summary:
ConEmu tries to normalize 256 colors to 16 colors but its normalization logic
is buggy. For example, color196 gets normalized to green instead of red. See
the attached picture of ConEmu and its debug real console.

{F235735030}

Reviewed By: markbt, ikostia

Differential Revision: D21311443

fbshipit-source-id: cb6db07d6b10a7365e33f4aa8f5f3f61f90c8e69
2020-05-01 14:51:51 -07:00
Jun Wu
5b881f086f pyzstore: further reduce cpython_ext::Bytes usage
Summary: This avoids data copies.

Reviewed By: DurhamG

Differential Revision: D21213075

fbshipit-source-id: 9575173f163d71543affabd9861931c11086f40a
2020-05-01 14:24:52 -07:00
Jun Wu
73ff6559e6 zstore: add simple caching
Summary: Add simple caching so zstore can avoid some zstd calculation.

Reviewed By: DurhamG

Differential Revision: D21213076

fbshipit-source-id: 5e3152949cf4e6d6193c3ef3401f24e2efac5620
2020-05-01 14:24:52 -07:00
Durham Goode
3ac2be361a configs: move fbrules to a Facebook only part of the crate
Summary:
We'll be adding a bunch of Facebook specific configuration and values
here. Let's move it to someplace not open source.

Reviewed By: quark-zju

Differential Revision: D21241038

fbshipit-source-id: 2ac9cdce40b1b46f15f171d9d1f6b6692dcd29bf
2020-05-01 13:17:21 -07:00
Durham Goode
b1a2785a19 configparser: add ensure_location_supersets function
Summary:
Implements an ensure_location_supersets function who's goal is to
verify that a given config location specifies the exact same configs as a given
set of other locations. Any inconsistencies are removed from the config and
reported to the caller.

This will be used to ensure our dynamic configs match our existing rc file
configs exactly, before we delete the file configs.

Reviewed By: quark-zju

Differential Revision: D21240837

fbshipit-source-id: e2c8ec054a3696d2cf02e65c212ad886c5117253
2020-05-01 13:17:21 -07:00
Mark Thomas
ed49d36d41 templatekw: parents template should not return null p2
Summary:
The `parents` template currently returns "meaningfulparents".  This sometimes
returns the null revision as the second parent, making templates think this is a
merge commit when it is not.  Make sure we filter this out.

Reviewed By: quark-zju

Differential Revision: D21347776

fbshipit-source-id: af83fd5cff381850ac39d97b5b2f4c77033fe2fb
2020-05-01 12:49:37 -07:00
Jason White
d5b2fb798e Fix outdated Cargo.toml files
Summary: `cargo autocargo` should normally produce no changes on `master`. The features of the `log` crate was updated in D21303891 without re-running autocargo. This fixes it.

Reviewed By: dtolnay

Differential Revision: D21349799

fbshipit-source-id: ce487bc5989e179673297350249593103b4d34dd
2020-05-01 10:29:33 -07:00
Stanislau Hlebik
bec619bc5e remotefilelog: print the progress when downloading files
Summary:
Now we download files in 200K chunks, and it might be confusing - it's unclear
whether hg is making any progress or not.
This diff makes it clearer.

Note I'm not printing progress before first fetch to avoid breaking the tests

Reviewed By: farnz

Differential Revision: D21348756

fbshipit-source-id: 05e5169114adf2b99a74b37d933755a644214a42
2020-05-01 07:56:26 -07:00
Kostia Balytskyi
b17df254d7 infinitepush: do not record crossbackendsync bundles
Summary: Mercurial Infinitepush normally records received bundles into the `forwardfillerqueue`, which is later tailed by the commit cloud `forwardfiller` in order be replayed onto Mononoke. Now I am adding a reverse filler, which means that we will have two such queues and sync in two directions. Therefore, in order to avoid infinite loops we need to distinguish cross-backend bundle replay from genuine pushes. I propose to use `crossbackendsync` bundle2 param to indicate that no recording is needed.

Reviewed By: krallin

Differential Revision: D21255446

fbshipit-source-id: 70f6efe1331bd3c7fd3aca61d486d350d93086dc
2020-05-01 06:46:50 -07:00
Stanislau Hlebik
03023aa095 commit cloud: fix hg cloud --help
Summary:
Previously we weren't showing help at all when running "hg cloud --help". This
diff should fix it

Reviewed By: ikostia

Differential Revision: D21347825

fbshipit-source-id: 1d9d11e2f9fe18a03b5d2cd8bd316fe9a218347c
2020-05-01 06:08:14 -07:00
Adam Kramer
05f7469a84 Make graph renderer link command-clickable
Summary:
In vs-code if you have a well-formed url you can command-click it
to open it, e.g., when you see it in your terminal. But this URL is not
well-formed because it's missing the protocol. So let's add it.

Reviewed By: quark-zju

Differential Revision: D21336742

fbshipit-source-id: dd2de2d5177d3a2542c4a22f0099f28b97c79d06
2020-04-30 23:59:36 -07:00
Kostia Balytskyi
9b914911f6 infinitepush: do not read the whole file to check size
Summary: If the bundle file has 1G in size, we probably don't want to waste 1G of memory just to see that it is too big.

Reviewed By: markbt

Differential Revision: D21177359

fbshipit-source-id: 026b5ab40e6dbddba3d7142a8e34256d127bf82c
2020-04-30 13:00:22 -07:00
Kostia Balytskyi
e016ea16a2 commit cloud forwardfiller: add a test to exercise the flow
Summary:
Building on the previous two commits, this adds a test which performs the following steps:
- does an infintiepush push to a Mercurial server
- looks into the `forwardfillerqueue`
- runs commitcloud forwardfiller's `fill-one` to replay the bundle to Mononoke
- verifies that this action causes the commit to appear in Mononoke

As this test uses `getdb.sh` from Mercurial test suite, it needs to be whitelisted from network blackholing (Note: we whitelist mononoke tests, which run with `--mysql` automatically, but this one is different, so we need to add it manually. See bottom diff of the stack for why we don't use `--mysql` and ephemeral shards here).

Reviewed By: krallin

Differential Revision: D21325071

fbshipit-source-id: d4d6cbdb10a2bcf955ee371278bf2bbbd5f5122c
2020-04-30 13:00:22 -07:00
Kostia Balytskyi
5041433ad6 tests: make hgsql and infinitepush libraries usable from Mononoke
Reviewed By: krallin

Differential Revision: D21303956

fbshipit-source-id: 61c3eaf5235f55636fd5b5c8d9ac899f39691d9d
2020-04-30 13:00:22 -07:00
Mark Thomas
ff08b99c08 remotenames: hide unwanted commits after clone with selectivepull
Summary:
Cloning a repository with selectivepull enabled temporarily disables
selectivepull for the duration of the initial pull so that we can get a
streaming clone.

Once this is done, hide all of the commits in the repository by clearing the
visible heads.  Selective pull will then populate the remote bookmarks with the
public heads that we do want.

Reviewed By: quark-zju

Differential Revision: D21301037

fbshipit-source-id: 565ae50439ed5405ce940a5675caeba912fe7083
2020-04-30 05:44:04 -07:00
Mark Thomas
ac62d5b52c commitcloud: allow omission of remote bookmarks
Summary: If a scratch remotebookmark points to a draft commit that is not available locally (i.e., has been hidden on a different machine), then don't pull it into the local repository.  Instead, just omit the remote bookmark.

Reviewed By: quark-zju

Differential Revision: D21287886

fbshipit-source-id: 84e7c6b52250709f7c88b07fccdbb61e044370c8
2020-04-30 05:44:03 -07:00
Mark Thomas
e025e68e33 commitcloud: allow sync to delete remote bookmarks
Summary:
Rewrite `_processremotebookmarks` to allow sync operations to delete
remote bookmarks.

Reviewed By: quark-zju

Differential Revision: D21283026

fbshipit-source-id: 7ded1d1cfb725f4b60ad2d55cf7d102c91113d6a
2020-04-30 05:44:03 -07:00
Mark Thomas
a4648a7805 remotenames: don't remove selectivepull subscriptions on push
Summary:
Selective pull subscripts are lost during a push.  This is because
`remotenames.expush` calls `selectivepullbookmarknames` with the
remote, not the remote name.

`remotenames.exclone` does the same, but in that case there is
no need for the remote name as the destination can't have any
subscriptions other than the default set.

Reviewed By: quark-zju

Differential Revision: D21283029

fbshipit-source-id: 52c2e7e301b8e95b4a252cbfe2ad9de168e81044
2020-04-30 05:44:02 -07:00
Mark Thomas
da9e7b2264 commitcloud: add test demonstrating bugs in remotebookmarkssync
Summary:
Commit cloud remote bookmarks sync has several edge cases that don't work correctly.

This test demonstrates some that we will fix.

Reviewed By: quark-zju

Differential Revision: D21283028

fbshipit-source-id: 4e746476a0f3bf0ca7d7088f510451632a2ee075
2020-04-30 05:44:02 -07:00
Zeyi (Rice) Fan
8830ed55df util: not try to create the directory when it already exists
Summary: Fix permission issues we are seeing with the latest Mercurial release.

Reviewed By: xavierd

Differential Revision: D21294499

fbshipit-source-id: bcfb13dd005258b2e3b74fa281dbd8df36133ef6
2020-04-28 20:33:59 -07:00
Carolyn Busch
4eeab3b81b Update cpython to 0.5
Summary:
D21270958 updated the cpython, python27-sys, and python3-sys crates to 0.5. Update
the Mercurial cargo dependencies to match.

Reviewed By: xavierd

Differential Revision: D21281875

fbshipit-source-id: ccad68749a25d11240351b5faeef27cb9c693456
2020-04-28 11:47:41 -07:00
Jun Wu
51dde1bcef interactiveui: do not import termios on Windows
Summary: `termios` is not avialable on Windows. Do not import it.

Reviewed By: DurhamG

Differential Revision: D21258999

fbshipit-source-id: f4390b69fe9abceea8b1959e7506c1558778f980
2020-04-28 10:26:21 -07:00
Jun Wu
d479053954 metalog: support exporting to a git repo
Summary:
I wanted to figure out "who added this visible head", "what is the difference
between this metalog root and that root". Those are actually source control
operations (blame, diff). Add a git export feature so we can export metalog
to git to run those queries.

Choosing git here as we don't have native Rust utilities to create a more
efficient hg repo yet.

Ideally we can also make hg operate on a metalog directory as a "metalogrepo"
directly. However that seems to be quite difficult right now due to poor
abstractions.

Reviewed By: DurhamG

Differential Revision: D21213073

fbshipit-source-id: 4cc0331fbad6e1586907c0a66c18bcc25608ea49
2020-04-27 20:25:25 -07:00
Jun Wu
dc8773033e phrevset: use unfiltered repo for lookups
Summary:
This would avoid issues where the Phabricator commit hash points
to a filtered commit locally. There are 2 user complaints already.

Reviewed By: DurhamG

Differential Revision: D21180833

fbshipit-source-id: 7374e236bcf5e3f3e62bae59fa53604869e22c6f
2020-04-27 20:04:18 -07:00
Jun Wu
3df5fcf779 pymetalog: add handy APIs for debugshell
Summary:
This makes metalog easier to use in debugshell context. For example, to
investigate the "bookmarks" in the past, the code gets simplified from:

  roots = b.metalog.metalog.listroots(repo.svfs.join('metalog'))
  past_ml = b.metalog.metalog(repo.svfs.join('metalog'), root[10])
  past_ml.get("bookmarks")

to:

  roots = ml.roots()
  past_ml = ml.checkout(roots[10])
  past_ml.get("bookmarks")

Reviewed By: DurhamG

Differential Revision: D21162568

fbshipit-source-id: 7cc5581afe596a3d2696311a36ac11caa718428a
2020-04-27 20:04:18 -07:00
Jun Wu
838cc44624 commitcloud: log metalog root during update references
Summary: This uploads more details that might be useful for debugging.

Reviewed By: DurhamG

Differential Revision: D21179514

fbshipit-source-id: ba24395fc4fd153c3ceb4957f822aab70821e6ef
2020-04-27 19:50:58 -07:00
Jun Wu
a0207c4542 metalog: expose root id API
Summary: This allows the Python world to obtain the root ID for logging purpose.

Reviewed By: DurhamG

Differential Revision: D21179513

fbshipit-source-id: 3f289c06d3d470ff492de39fa985203b3facbf00
2020-04-27 19:50:58 -07:00
Jun Wu
7a60f8ac27 commitcloud: log update reference calls
Summary:
This would hopefully give us more insights about when and what host uploads
10k+ remote bookmarks.

Reviewed By: DurhamG

Differential Revision: D21179515

fbshipit-source-id: 3e5c5559e2e739268add05e40f71bea08c29662f
2020-04-27 19:50:57 -07:00
Jun Wu
d8bdae0449 indexedlog: remove chown feature
Summary:
We removed the feature in D20704618 and it does not cause complaints.
Let's remove the code supporting the chown feature.

Reviewed By: DurhamG

Differential Revision: D21170307

fbshipit-source-id: c845016219e8c681930bb1780b94e6d31ca99730
2020-04-27 15:47:59 -07:00
Xavier Deguillard
86965b2f80 revisionstore: query store before fetching
Summary:
While the change looks fairly mechanical and simple, the why is a bit tricky.
If we follow the calls of `ContentStore::get`, we can see that it first goes
through every on-disk stores, and then switches to the remote ones, thanks to
that, when we reach the remote stores there is no reason to believe that the
local store attached to them contains the data we're fetching. Thus the
code used to always prefetch the data, before reading from the store what was
just written.

While this is true for regular stores (packstore, indexedlog, etc), it starts
to break down for the LFS store. The reason being that the LFS store is
internally represented as 2 halves: a pointer store, and a blob store.  It is
entirely possible that the LFS store contains a pointer, but not the actual
blob. In that case, the `get` executed on the LFS store will simply return
`Ok(None)` as the blob just isn't present, which will cause us to fallback to
the remote stores. Since we do have the pointer locally, we shouldn't try to
refetch it from the remote store, and thus why a `get_missing` needs to be run
before fetching from the remote store.

As I was writing this, I realized that all of this subtle behavior is basically
the same between all the stores, but unfortunately, doing a:
  impl<T: RemoteDataStore + ?Sized> HgIdDataStore for T
Conflicts with the one for `Deref<Target=HgIdDataStore>`. Macros could be used
to avoid code duplication, but for now let's not stray into them.

Reviewed By: DurhamG

Differential Revision: D21132667

fbshipit-source-id: 67a2544c36c2979dbac70dac5c1d055845509746
2020-04-27 12:53:11 -07:00
Qing Dong
b4edb7ff7f revisionstore: implement the get() functions on the various LocalDataStore interface
Summary: implement the get() functions on the various LocalDataStore interface implementations

Reviewed By: quark-zju

Differential Revision: D21220723

fbshipit-source-id: d69e805c40fb47db6970934e53a7cc8ac057b62b
2020-04-27 12:35:24 -07:00
Jun Wu
90c062ca08 debugshell: accept file name
Summary:
This makes debugshell easier to use. Especially when the script wants to access
`__file__`.

Reviewed By: DurhamG

Differential Revision: D21169556

fbshipit-source-id: 88b3ebb1ca9a39fe26bc7cc5ea8e250c28fa0d6f
2020-04-27 10:58:56 -07:00
Jun Wu
af1ad137bf remotenames: normalize infinitepush/ to default/
Summary:
Similar to part of D20804854, normalize infinitepush/ to default/ in
remotenames code paths.

This might not be strictly necessary, but we never want to write out
infinitepush/ remote bookmarks and we have seen that OnDemand writes out
infinitepush/ remote bookmarks (which got synced to commit cloud and caused
user complaints) recently.

Reviewed By: DurhamG

Differential Revision: D21233658

fbshipit-source-id: cf8c12ac40348c7b475a7f3d5d5223775491650e
2020-04-27 10:37:27 -07:00
Saurabh Singh
79f5217ccc test-bisect-t: use debugshell instead of debugpython
Summary:
The python script run using `debugpython` within the test was importing
Mercurial internal modules which causes failures on some platforms like
Windows. Instead, lets use the `debugshell` command which already imports the
common objects like `repo` correctly.

Reviewed By: xavierd

Differential Revision: D21251818

fbshipit-source-id: db1b9e92df99b736a28bc9e89fb08ae77d6e82fc
2020-04-27 07:55:30 -07:00
Xavier Deguillard
39d49b694a revisionstore: remove memcache dependency on @mode/mac
Summary:
Memcache isn't available for Mac, but we can build the revisionstore with Buck
on macOS when building EdenFS. Let's only use Memcache for fbcode builds on
Linux for now.

Reviewed By: chadaustin

Differential Revision: D21235247

fbshipit-source-id: 5943ad84f6442e4dabbd2a44ae105457f5bb9d21
2020-04-24 17:29:36 -07:00
Zeyi (Rice) Fan
3374b99f28 util: ensure correct permission is set when creating directories
Summary:
When creates directories sometime we want to make sure other users within the same group have the write access to it to enable data sharing. Previously we rely on setting umask for the entire process to make sure the newly created directories have the correct permission bit. This is kind fragile and error-prone when running in a multi-thread environment.

This diff introduces an internal function `create_dir_with_mode` to create directory with specified permission mode. It first creates a temporary directory within the parent of the directory being created, setting up the correct permission bit, then attempts to rename the temporary directory to the desired name. This ensures that we never leave a directory without the correct permission in the place we need and without changing umask for the process.

Reviewed By: xavierd

Differential Revision: D21188903

fbshipit-source-id: 381bff7d3aaca097b9d50150e86cbbf70a90a0a5
2020-04-24 17:17:05 -07:00
Durham Goode
344837cca4 filesystem: python bindings for new rust pending changes
Summary:
Adds initial python bindings for the rust pending changes. This is not
ready for production usage yet, but having the bindings let's me test changes
more easily until we're ready for automated tests.

Reviewed By: xavierd

Differential Revision: D20546896

fbshipit-source-id: c0ad7155e5068f45bf9c987030746e6c5f35c26a
2020-04-24 13:58:53 -07:00
Durham Goode
092d350800 filesystem: add treestate walking logic
Summary:
The second phase of pending changes is to iterate over the treestate
and figure out what files were not seen in the filesystem walk. This diff
implements that.

Reviewed By: xavierd

Differential Revision: D20546899

fbshipit-source-id: 3523fbc7e31ef0ed09c4937c72264b64e2a3db5b
2020-04-24 13:58:53 -07:00
Durham Goode
73a45b695b filesystem: add filesystem walking to PendingChanges
Summary:
The first phase of pending changes is inspecting the filesystem for
changes. This diff adds that logic.

Reviewed By: xavierd

Differential Revision: D20546909

fbshipit-source-id: 1c2c0fa7f700dbff4acfce4d5271b4472a13571f
2020-04-24 13:58:53 -07:00
Jun Wu
3de6513f0e revset: make autopull commit pull seletivepull bookmarks
Summary: This ensures lagged master issue does not happen by pulling a single commit.

Reviewed By: DurhamG

Differential Revision: D20845384

fbshipit-source-id: 3ba16c07fe264fe2b6aecd494bbb832af7b390a0
2020-04-24 11:16:23 -07:00
Jun Wu
3514ac0de8 remotenames: autopullprefix -> autopullpattern
Summary:
Patterns are more powerful than prefixes. Some full revset names are
obviously not valid remote name - for example, `remote/master :: .`.
They cannot be ruled out using prefixes but can using patterns.

As we're here, add some caching for the regex compiler.

Reviewed By: DurhamG

Differential Revision: D20831015

fbshipit-source-id: af8c4eed4a3153fd71480b8972c55feed4641392
2020-04-24 11:16:22 -07:00
Jun Wu
368d59e436 revset: attempt to pull the revset expression as a single name
Summary:
For names like `a-b-c`, it can be parsed in multiple ways:

- `"a-b-c"`
- `"a-b" - "c"`
- `"a" - "b-c"`

Mercurial uses `repo.lookup` in the parser to accept names like `"a-b-c"`.
Do it for the whole revset expression too.

But do not do it for every lookup (ex. testing `"a-b"` or `"b-c"` in the above
case), because that can be exceedingly expensive.

Reviewed By: DurhamG

Differential Revision: D20831014

fbshipit-source-id: f507e04ce24c953b096ccd836c356f50f11d2006
2020-04-24 11:16:22 -07:00
Jun Wu
47f73cc5e8 revset: calculate autopull names before evaluating revset
Summary:
Add a function to figure out unknown symbols of a revset AST without executing
the revset, then try to autopull unknown symbols.

This has pros and cons comparing to the runtime (stringset) approach.

Pros:
- The fullreposet restriction is lifted.
- We can have some ideas about whether the symbols are "probably" locally
  known. If everything is locally known, then we can skip all remote lookups
  (see the next diff for details).
- We can fetch multiple symbols in batch if we want. Although it's difficult
  to do so right now due to different paths (paths.default, infinitepush,
  infinitepushbookmark, etc). Revisit once the paths are unified.

Cons:
- Functions and argument types must be statically known. Might miss
  some functions added by some extensions.

Reviewed By: DurhamG

Differential Revision: D20909535

fbshipit-source-id: a567ca0aa80aa7f2930dd75637ef3ff8cef1bdef
2020-04-24 11:16:22 -07:00
Jun Wu
e26f2eeacb tests: add a test about autopull on remote names with dashes
Summary:
Dashes are also revset operators. Add a test so we can verify if we can also
autopull those special names.

Reviewed By: DurhamG

Differential Revision: D20831016

fbshipit-source-id: 97e772053dae873ebaa529ac9eb84ea9d04f0f63
2020-04-24 11:16:22 -07:00
Jun Wu
a2d9f6604e infinitepush: disable old autopull if the new autopull is enabled
Summary:
Similar to the previous diff, let's use the new autopull logic, which
uses the tech-debt free repo.pull API.

In rare cases this can cause more pulls due to the path selection (ex. use
"infinitepush" for "remote/scratch/x" but "default" for "remote/stable/x").
With Mononoke serving repos, we might be able to remote the special paths,
and just use "default" for all cases.

If it goes well, we can then delete the old code.

Reviewed By: DurhamG

Differential Revision: D20804854

fbshipit-source-id: 75e68582a29b613c8626a119b85064e3c0ba9462
2020-04-24 11:16:21 -07:00
Jun Wu
d90be19909 remotenames: disable old autopull if the new autopull is enabled
Summary:
The new auto pull logic can replace the one in remotenames. It it goes well, we
can then remove the code in remotenames doing the auto pull.

Reviewed By: sfilipco

Differential Revision: D20804853

fbshipit-source-id: c87b6b382f4cce3b306648b305a7b6bbaec05df1
2020-04-24 11:16:21 -07:00
Jun Wu
614b349628 revset: implement auto pull for hoisted remote bookmarks
Summary:
Attempt to auto pull bookmark names without the `remote/` or `default/` prefix
if hoist is set. This would hopefully be enough to allow us to enable
selectivepull globally without breaking existing users.

Reviewed By: sfilipco

Differential Revision: D20804856

fbshipit-source-id: 72601ac5e3545523cbfd7087d1fc822ef33c2f2e
2020-04-24 11:16:21 -07:00
Jun Wu
880b33b0b7 revset: implement auto pull for commit hashes using repo.pull API
Summary: This might remove less clean logic in infinitepush.

Reviewed By: sfilipco

Differential Revision: D20804855

fbshipit-source-id: da5ed24dd912b5f86839e671a9d6b0e9356b7c86
2020-04-24 11:16:20 -07:00
Xavier Deguillard
19bfd35298 revisionstore: multiplex stores should return a path on flush
Summary:
On repack, when the Rust stores are in use, the repack code relies on
ContentStore::commit_pending to return the path of a newly created packfile, so
it won't delete it when going over the repacked ones. When LFS is enabled, both
the shared and the local stores are behind the LfsMultiplexer store that
unfortunately would always return `Ok(None)`. In this situation, the repack
code would delete all the repacked packfiles, which usually is the expected
behvior, unless only one packfile is being repacked, in which case the repack
code effectively re-creates the same packfile, and is then subsequently
deleted.

The solution is for the multiplex stores to properly return a path if one was
returned from the underlying stores.

Reviewed By: DurhamG

Differential Revision: D21211981

fbshipit-source-id: 74e4b9e5d2f5d9409ce732935552a02bdde85b93
2020-04-23 15:14:28 -07:00
Xavier Deguillard
413d2b3aba remotefilelog: enable uploading LFS blobs
Summary:
This adds the proper hooks in the right place to upload the LFS blobs and write
to the bundle as LFS pointers. That last part is a bit hacky as we're writing
the pointer manually, but until that code is fully Rust, I don't really see a
good way of doing it.

Reviewed By: DurhamG

Differential Revision: D20843139

fbshipit-source-id: f2ef7b045c6604398b89580b468c354d14de1660
2020-04-23 14:00:23 -07:00