Analysis of Git and Mercurial

Note: this analysis was done in summer 2008, when we first began scoping work for DVCS support in Google Code.

Introduction

This document summarizes the initial research for adding distributed version control as an option for Google Code. Based on popularity, two distributed version control systems were considered: Git and Mercurial. This document describes the features of the two systems, and provides an overview of the work required to integrate them with Google Code.

Distributed Version Control

In traditional version control systems, there is a central repository that maintains all history. Clients must interact with this repository to examine file history, look at other branches, or commit changes. Typically, clients have a local copy of the versions of files they are working on, but no local storage of previous versions or alternate branches.

Distributed Version Control Systems (DVCS) use a different structure. With DVCS, every user has their own local repository, complete with project history, branches, etc. Switching to an alternate branch, examining file history, and even committing changes are all local operations. Individual repositories can then exchange information via push and pull operations. A push transfers some local information to a remote repository, and a pull copies remote information to the local repository. Note that neither repository is necessarily "authoritative" with respect to the other. Both repositories may have some local history that the other does not have yet. One key feature of any DVCS system is to make it easy for repositories to unambiguously describe the history they have (and the history they are requesting). Both Git and Mercurial do this by using SHA1 hashes to identify data (files, trees, changesets, etc).

DVCS's provide a lot of flexibility in developer workflows. They can be used in a manner similar to traditional VCS's, with a central "authoritative" repository with which each developer synchronizes. For larger projects, it is also possible to have a hierarchy of server repositories, with maintainers for each repository accepting changes from downstream developers and then forwarding them upstream. DVCS's also allow developers to share work with each other directly. For example, two developers working on a new feature could work on a common branch and share work with each other independent of an "authoritative" server. Once their work was stable, it could then be pushed to a public repository for a larger audience.

Because there is no central repository, the terms client and server don't necessarily apply. When talking about two repositories, they are typically referred to as local and remote rather than client and server. However, in the context of implementing a DVCS for Google Code, the repository hosted at Google will be considered the server, and a user's repository will be called the client.

Feature Comparison

There is actually a great deal of similarity between Git and Mercurial. Instead of providing a long list of features that are equivalently supported in both system, this section attempts to highlight areas of significant difference between the systems.

Git Advantages

Note: Mercurial has added rebase support since this analysis was conducted.

Mercurial Advantages

Other Differences

Implementation

Data Storage

Both Git and Mercurial internally work with very similar data: revisions of files along with a small amount of meta information (parents, author, etc). They both have objects that represent a project-wide commit, and these are also versioned. They both have objects that associate a commit with a set of file versions. In Git, this is a tree object (a tree structure with tree objects for directories and references to file revisions as the leaves). In Mercurial, there is a manifest (a flat list mapping pathnames to file revision objects). Aside from the manifest/tree difference, both are very similar in terms of how objects are searched and walked.

Git uses a combination of storing objects directly in the file system (indexed by SHA1 hash) and packing multiple objects into larger compressed files, while Mercurial uses a revlog structure (basically a concatenation of revision diffs with periodic snapshots of a complete revision). In both cases, the native storage will not be used and the objects will be stored in Bigtable instead. Due to the similarity of the basic Git and Mercurial data objects, the effort to solve such problems should be the same regardless of which DVCS is being used.

The only major difference for the data storage layer is the implementation language. If a significant amount of Git/Mercurial code is to be reused, then a Git implementation would be in C, and a Mercurial one would be in Python (or perhaps C++ with SWIG bindings).

Mercurial Integration

Mercurial has very good support for HTTP based stateless pushing and pulling of remote repositories. A reasonable amount of effort has been made to reduce the number of round trips between client and server in determining what data needs to be exchanged, and once this determination has been made all of the relevant information is bundled into a single large transfer. This is a good match for Google's infrastructure, so no modifications will be required on the client side.

Git Integration

Git includes support for HTTP pulls (and WebDAV pushes), but the implementation assumes that the server knows nothing about Git. It is designed such that you can have a Apache simply serve the Git repository as static content. This method requires numerous synchronous round trip requests, and is unsuitable for use in Google Code (1).

Git also has a custom stateful protocol that supports much faster exchanges of information, but this is not a good match for Google infrastructure. Specifically, it is very desirable to use a stateless HTTP protocol since there is already significant infrastructure in place to make such transactions reliable and performant.

Note: There has been some discussion about improving HTTP support in the Git community since this analysis was done.

Summary

In terms of implementation effort, Mercurial has a clear advantage due to its efficient HTTP transport protocol.

In terms of features, Git is more powerful, but this tends to be offset by it being more complicated to use.


1 As a benchmark, Git and Mercurial repositories were seeded with approximately 1500 files totaling 35 M of data. The servers were running in Chicago and the clients in Mountain View (51 ms ping time). The operation of cloning the remote repository (similar to a initial checkout in traditional version control systems) averaged 8.1 seconds for Mercurial and 178 seconds for Git (22 times slower). A single file in the repository was then changed 50 times and the clients pulled the updates. In this case, Mercurial took 1.5 seconds and Git required 18 seconds (12 times slower). When the Git protocol was used instead of HTTP, Git's performance was similar to Mercurial (8.7 seconds for cloning, 2.8 seconds for the pull).


Comment by carl.j.meyer, Apr 24, 2009

Very reasonable analysis. Can I ask the obvious: why was Bazaar not even considered?

Comment by cuerty, Apr 24, 2009

The benchmark sounds better for Mercurial in terms of HTTP transmission an performance, but the adventaje of using a DVCS is exactly the oposite: to do less commits to the "central" repository, and work offline most of the time, I think that it would be a good idea to post similar tests but on local operations (branching, reverting patchs, etc).

The big adventaje, and the one I think makes Mercurial a better choice, is the support in platforms as Windows using GUI tools like Tortoise Hg.

Comment by kmcfadden, Apr 24, 2009

@carl, I don't know about reasonable analysis, but I can't argue with the result.

Git is far easier than SVN or CVS, especially when creating repositories. How hard is push, pull, merge, branch, checkout, and clone? I would also say that namespacing is a huge win for Git. I've always disliked SVN defaulting to putting all projects into one repository. It just seems dirty.

The issue with Windows support is valid. I'm not happy with either Cygwin or Msysgit's reliability.

Regardless, Hg is a perfectly acceptable choice and doesn't really need justification. Google is primarily a Java/Python company. Hg is Python. Ergo, Hg is the proper DVCS for Google.

Comment by jasonpaulwatkins, Apr 24, 2009

This misses the point. None of the technical differences appear to be blockers, despite any particular wrinkles of google's infrastructure.

The question that matters is:

potential new customers * lifetime customer value > implementation cost

Github's explosive growth makes it clear it would likely show a return. I suspect hg and bzr would both as well. Darcs likely not.

It's too easy for us technical folks to focus on a technical winner while forgetting that the technical differences only set costs in a larger proposition.

Comment by antonin.hildebrand, Apr 24, 2009

During last months I've moved all my projects from Google Code to GitHub?. I'm quite happy GC picked Hg, because it will make Git community more concentrated on GitHub?.

Google is a Python company and it should obviously go with Hg.

Comment by diogofsr, Apr 24, 2009

I am with carl.j.meyer.

It would be nice to have the same analysis with bazaar.

One could obviously say that would not be feasible to talk about every DVSC out there, but bazaar/mercurial/git are the nowadays show makers.

Comment by casey.mcginty, Apr 24, 2009

Unfortunately there was no mention of Git index and 'add -i'? Does Hg have a comparable feature?

Comment by ngrilly, Apr 24, 2009

We can guess Google didn't consider Bazaar because a project hosting service already exists with Bazaar support: it's Launchpad. Personally, I'm really happy with Mercurial, and it's a good news that Google Code now supports a DVCS.

Comment by diogofsr, Apr 24, 2009

Using this line of thinking, hosting a svn hosting service would also not be necessary as we have Sourceforge - which, by the way, now supports cvs/subversion/bazaar/git/mercurial.

Comment by maxosmail, Apr 24, 2009

yet for some reason we have sourceforge, github, and bitbucket

why cant we all share code in a central repository... hahaha!

Comment by ches.martin, Apr 24, 2009

@casey.mcginty - It's a bit off topic for this article, but Hg does not have a 'staging area' concept analogous to Git's index. To start tracking a new file, you need to hg add it. If you modify a tracked file, it will be part of the next commit if you aren't explicit about omitting it. It's an easy transition from SVN in this way, but hg is generally a little more flexible when you want it to be.

For functionality like git add --interactive, see the Darcs-inspired Record extension for hg, which is included in the official distribution, but needs to be enabled in config. I do wish you could split hunks with it like git add -i can, but it's still pretty nice. Of course there are also graphical tools that allow you to select changes to commit.

Comment by sbronson, Apr 24, 2009

The fourth Mercurial advantage is completely wrong.

"Unfortunately, this also means that Git is perfectly happy to lose history."

No, git goes to great lengths to not lose history. Check out the reflog. Even when you intentionally rewrite some revision history (very rare but incredibly useful when you need it), all the old history remains in the repo and you can always switch back to it.

"Mercurial's repository is structured more as an ever-growing collection of immutable objects."

So is Git's. Their repository formats are amazingly similar.

"git-push --force can result in revisions becoming lost in the remote repository".

Er, that's why you need to force it? As with just about everything else in life, if you're forcing something, you're probably doing it wrong.

"a custom Git server could be written to disallow the loss of data, so this advantage is minimal"

Dude, removing the --force option from git push should be a three-line patch. Go for it. While you're at it, you might want to remove the --force command from rm also; it can potentially result in irrevocable data loss too.

Comment by rwparris2, Apr 24, 2009

I'm also curious why bazaar wasn't considered. It has one great feature that AFAIK neither Hg nor Git has -- you can limit users to a specific directory.

Obviously that isn't useful for everyone, but I've found it very convenient for a number of projects I've been involved in.

Comment by Lenz.Grimmer, Apr 25, 2009

Insightful analysis. Too bad that Bazaar was not included, it would have certainly rated high in the given criterias.

Comment by memorius, Apr 25, 2009

A couple of corrections to the discussion of git from a longtime user (on a largish project with multiple users using a central repos, which I administer):

Re. 'git push' and deletion of data from the remote repos: there is no need for a 'custom server'; git has a built-in repository option to disable this (even with 'push --force'): set config property receive.denyNonFastforwards to false on the repos being pushed to; this is actually set by default on bare repositories (the usual setup for central shared ones). There is also a hook script in the git distribution to disallow other kinds of deletes by pushing: the 'update' hook; you can set hooks.allowdeletebranch, hooks.allowdeletetag, etc.

Re. maintenance / git-gc: it is not usually necessary to run this manually on a local repository just for performance: various commands will do it automatically for you as needed. You only need to run 'gc --prune' if you want to delete data that is no longer reachable, e.g. old versions of rebased or deleted branches. This ties in with the 'losing data' point: it will never do a 'prune' unless you explicitly tell it to.

Comment by jeng...@medozas.de, Apr 25, 2009

In relpy to comment by sbronson:

>Dude, removing the --force option from git push should be a three-line patch.

Even so, one could still force an update (by deleting and repushing the branch, though that's not quite atomic). Also, sourceforge's git repos are, by default, configured to reject --forced pushes, so that is also a way to harden history loss.

Comment by sitaramc, Apr 25, 2009

in reply to jeng...@medozas.de's reply to sbronson

don't want to allow deleting a branch? Just say "git config receive.denyDeletes true". As simple as receive.denyNonFastForwards.

the original article's statement that "custom git server could be written..." made it seem like you'd at least have to compile something :-) Which is very far from the truth

Sitaram

Comment by dyqith, Apr 25, 2009

What are the main criterias for using either DVCS for google code?

The article listed some adv/disadv of each DVCS, and some of their implementation differences, but are the challenges for using either for google code? i.e., Are you trying to find which is more useful to the user? (popularity vs. feature set vs. speed) or Easier to implement on google's end? (support vs algorithm speed vs install challenges)

Comment by masklinn, Apr 25, 2009
I think that it would be a good idea to post similar tests but on local operations

There isn't that big a difference there, and an order of magnitude difference on the initial clone is painful.

Does Hg have a comparable feature?

Of course it does. Do you think the git guys invented interactive commits?

Hg does not have a 'staging area' concept analogous to Git's index.

No it doesn't.

I'm also curious why bazaar wasn't considered.

It's slow, it has less marketshare than either git or mercurial and it doesn't have any advantage on either?

Comment by rupert.thurner, Apr 25, 2009

is mercurial queue similar to a git staging area?

Comment by phrodo, Apr 25, 2009

Useless and EXTREMELY old report.

Comment by m.gebetsr, Apr 26, 2009

HGs static-http is as slow as git's cloning from http because git has a dump webserver on the other end. HG uses hg over a cgi to pipe their custom transport.

BUT... have you ever used hg for a longer timer?

We had e.g the problem, that older mercurials can't clone a new repo (over http) as the internal serilization changes (addition of branches). So old mercurials just exit with a backtrace, but this incompatibility was not documented nor fixed.

HG has changed their ondisk layout on every release which requries a local reclone (with --pull) of all local repositories to update.

The biggest problem with mercurial is their unuseable branch support and their unwillingness to fix long standing design errors.

- One repo per branch brings a quite high administration overhead not to mention the ressource wastage.
- Branch lookup in mercurial is pain slow per design, they tried to cure it with a branchcache, but a cache is not the same as O(1) lookup as in git.
- Tag handling is horrible. Mercurial save their tags in the file .hgtags in the repository root.

To locate a tag mercurial checks out all heads of all branches, to get their .hgtags file and merges them internally to just print the tags.

Another problem is, that it's impossible to cleanly remove a wrong tag from a repository.
- There are not only branches, but every branch can have multiple heads (unnamed branches) which is a pain to mess with.
- Automatic creation of heads seems as a quite safe feature in the beginning. But new users tend to lose changes in unnamed heads and for advanced users the destinction of heads and branches is just annoying.
Comment by snaury, Apr 26, 2009

Whenever people start talking about how mercurial is easier to use on Windows than git, autocrlf conversion comes to my mind. I admit that last time I checked it was maybe half a year ago, but compared to git's it was almost non-existant (seriously, checking for nul character is enough to tell if file is binary or not?!), and buggy, as those conversions didn't always fire and I'd be looking at a diff as if my entire file has changed (and things like that). In git I didn't experience such problems, and git has excellent .gitattributes file, where I can specify which files are what, exactly.

Again, a half or a whole year is a long time for things to have changed, but somehow I doubt crlf situation is any better these days...

Comment by rocallahan, Apr 26, 2009

> HG has changed their ondisk layout on every release which requries a local > reclone (with --pull) of all local repositories to update.

Not true, this certainly did not happen with the upgrade from 1.0 to 1.1.

Comment by m.gebetsr, Apr 26, 2009

my mercurial experience is a good match to this DVCSAnalysis as it is equally old (we switched to git about a year ago)

Comment by smenjas, Apr 27, 2009

I got the sense that the reviewer's biased towards Mercurial. I have no preference either way -- I've used both, but don't have enough experience to judge. I certainly hope Google will support both, however.

Comment by stephen.13, Apr 27, 2009

Surely you are going to support Git eventually though, aren't you? I mean, there are far more projects using it than Mercurial. In fact, lack of Git support is the main reason that I stopped using Google Code...

Comment by peter_ko...@hotmail.com, Apr 27, 2009

- One repo per branch brings a quite high administration overhead not to mention the ressource wastage.

not sure why it's hard to administrate multiple folders. clones are implemented as hard links, so no resource is wasted (and that is what it makes cloning fast).

- Branch lookup in mercurial is pain slow per design, they tried to cure it with a branchcache, but a cache is not the same as O(1) lookup as in git.

the mercurial way of doing things is one branch per repo plus using bookmarks/hgtasks/localbranches extensions for local branches. if you want to work on long lived branches and you can not or do not want to clone on the remote server, just share your long lived branches with others directly using hg serve.

- Tag handling is horrible. Mercurial save their tags in the file .hgtags in the repository root. To locate a tag mercurial checks out all heads of all branches, to get their .hgtags file and merges them internally to just print the tags.

it is fast enough, I have never experienced any issues with tags' performance.

- Another problem is, that it's impossible to cleanly remove a wrong tag from a repository.

That's because in mercurial history is sacred. I am not sure whether it's objectively a wart though.

- There are not only branches, but every branch can have multiple heads (unnamed branches) which is a pain to mess with.

that's annoying, I agree with that, however, most people do not use branches or multiple heads for long lived branches, so in reality this issue does not really come up.

- Automatic creation of heads seems as a quite safe feature in the beginning. But new users tend to lose changes in unnamed heads and for advanced users the destinction of heads and branches is just annoying.

please see above.

I guess the main point is that mercurial is not git and vica versa.

Comment by peter_ko...@hotmail.com, Apr 27, 2009

HGs static-http is as slow as git's cloning from http because git has a dump webserver on the other end. HG uses hg over a cgi to pipe their custom transport.

the recommended way of serving hg over http is via mod_wsgi

Comment by peter_ko...@hotmail.com, Apr 27, 2009

(Also, in many situations even "hg serve" is overkill, you can just share your work with others using mq, which is a patch queue. Worth noting that patches can be checked into the repository)

Comment by antiso, Apr 27, 2009

There is some disadvantage of Mercurial. It requires whole repository to be cloned. There is an activity for implementing PartialClone but it's still not official.

Comment by snmp1612342, Apr 28, 2009

one to educate the masses and the writer of this analysis: with the ping utility you don't measure "ping time" but Round Trip Time (RTT).

http://en.wikipedia.org/wiki/Ping http://en.wikipedia.org/wiki/Round-trip_delay_time

Comment by trejkaz, Apr 28, 2009

"We must support either Git or Mercurial" is actually a false dilemma, as it would be possible to support both. This is proven by Google Code already supporting subversion, which shows that they did not plan to support only one SCM system in the first place.

Comment by kiange, May 01, 2009

Just a note, I've translated this article into Traditional Chinese: http://blog.twpug.org/416

Comment by garen.parham, May 02, 2009

SVN has a SCC/VSIP provider. For those of us who use Visual Studio, and correspondingly don't use the crummy command-prompt in Windows, it'd be really nice if someone were to implement one for Mercurial.

Comment by yanckin, May 08, 2009

There's a complete comparison for bzr/hg/git

http://www.python.org/dev/peps/pep-0374/

Comment by jnareb, May 10, 2009

What was the badwidth of link used in clone/pull benchmark? DVCS might save bandwidth by sending only difference (only update) at the cost of extra exchange about what client have (versus what server have).

How were benchmarking done: are the times given in footnote wall time, or user+sys time? Are there more detailed results of this benchmark?

Comment by yarkot1, May 26, 2009

I am happy to see this analysis, and decision. I am not surprised that it pushes a few "dvcs wars" buttons (everyone has a favorite; eveyrone has their uses; all things change - heck, I remember when RCS was hot!;-)

I'm glad to see the migration, and see it incrementally (some have point out "why not all of them?" - sure, just not all at once, plese ;-).

As for private use, Google Android developers use (nay, develop!) git - so don't think that Google is somehow "favoring" one over another. The point here is, for public deployment and use, what is a better bet.

Even though the use cases are different, you might find the discussion on the same choices being made in the Python repositories at http://www.python.org/dev/peps/pep-0374/ and the decision for the Python repository to move to hg at pep-0385.

I for one use all 3. For open source projects, I am curious when hg will be available on code.google.com, as starting people on "remote sprinting" will be useful, and ANY DVCS will help. NOTE (for those moving or mirroring from SVN to hg) SVN and bzr version empty? directories; hg and git (and cvs) do not.

Comment by chrisincambo, Jun 02, 2009

We live in a world of convention over configuration. Everybody knows that insert favourite dvcs here can be configured to any of the things that attracted Google to Mercurial. But at the end of the day Mercurials standard conventions were closest to what Google was looking for.

Comment by vietor.liu, Jul 12, 2009

I hope googlecode support git. Why? "git on windows" is no problem, cygwin/msysgit+tortoisegit. More? easy,popular,powerful. googlecode+github is bad idea.

Comment by werner.punz, Jul 24, 2009

Actually I have been using git for quite a while and I agree here with googles choice for other reasons.

Some things I git are outright dangerous and even users who have been using git for some time might fall over it.

Here is an example, everyone and even the semi official git book on the net says that reverting like in SVN should be done with git reset --hard, while this is somewhat true but the git reset operation alters history which is a bigger problem once you have pushed your changes (and the next user pulling your data in or you yourself having to fight with the revlog if you have done an accidental reset will agree)!

Better checkout, but then if you checkout the wrong way (by forgetting a file or directory directive) you will automatically get a floating head and users suddenly will start to hack into the floating head. Git simply makes assumptions without telling you and the only way to find out is by rechecking out the master or finding the line in the man page describing it!

As for now I dont see any reasons to switch from git, but for a joined team development effort I would think twice of introducing it. First the cygwin port is simply not up to par to the other versions neither is msysgit which also just is a cygwin like layer which does not help the stability nor the performance (I cannot understand why everyone raves about msys which is as native as cygwin).

Secondly git provides a lot of flexibility but all this comes with a price. Now the average development team has several good experienced people a higher number of mediocre ones and a small number of bad ones, how do you deal with the dangerous operations git provides without any limitation to their access or warnings? You have to resolve that on project level while hg pushes you onto tracks.

As I said I am used to git, and will not change since hg does not really provide anthing worth switching over to me(and face it git-svn while having bugs is a huge plus especially if you are stuck with svn servers and want to sync them into your local repo), but I dont think it is that easy and painless to use like some videos on the net seem to indicate ;-) What you really have to to is to limit yourself to behavior which definitely does not alter anything in the history, so to say the subset of commands and behaviors hg enforces and you have to enforce that in your project, but on the other hand then you could use hg which does the enforcement for you. On the other hand the lack of lightweight branches in the core of hg is annoying as hell, sure there is a plugin, but this feature needs to be in the core :-(

Comment by vietor.liu, Jul 25, 2009

To werner.punz:

Better checkout, but then if you checkout the wrong way (by forgetting a file or directory directive) you will automatically get a floating head and users suddenly will start to hack into the floating head. Git simply makes assumptions without telling you and the only way to find out is by rechecking out the master or finding the line in the man page describing it!

'wrong way' :-) create develop branch and working in it. if you wrong, merge the master.

Comment by vietor.liu, Jul 25, 2009

To werner.punz:

....

1. Thinking that, 'why linux kernel used it'. 2. To design a workflow for team. 3. If you true, the git is a bad tool, not used it.

Comment by werner.punz, Jul 25, 2009

> why don't you know 'git revert'? why don't you create develop branch and working in it?

Git revert is not the same as svn revert, git revert kills the last checkin or the checkin you give it as parameter. If you do that you definitely do not get a revert but instead you have killed your last commit as well!

This is one of the biggest mistakes people make coming from svn assuming git revert is the same as svn revert while it is not!

The main problem I have with the documentation is that git reset hard should resemble svn revert while it clearly does not and introduces a dangerous operation. Git checkout -f HEAD . clearly is the equivalent and does not fiddle around with the history, this is funnily also the same how HG handles reverts in a save manner. There was a time when reset --hard HEAD was the recommended way until people started to shoot themselves into the foot with it, literally.

>1. Thinking that, 'why linux kernel used it'. 2. To design a workflow for team. 3. If you true, the git is a bad tool, not used it.

That is the main problem with git, git is very centered around the workflow of the linux people and basically evolved from a mess of low level tools into something which can be used (with pitfalls) while mercurial tries to enforce certain rails which prevent desaster. It still is a mess with a recommended way, and that is the reason why I currently would not use it within a team centric environment and why I have less problem with mercurial. On the other hand I can see its power and I have learned it so I have no problem to use it myself and prefer it over HG for personal use.

Comment by vietor.liu, Jul 25, 2009

mailto: git@vger.kernel.org

Comment by werner.punz, Jul 26, 2009

Actually user interface the problems are known, to the git people, no need to contact the mailinglist. I btw. found another git porcellain project which cleans up most if not all of the problems http://www.gnome.org/~newren/eg/ so far from what I have seen I like, it is how the commands from git should be.

Comment by sbellem, Sep 11, 2009

I just started to work for a company and I have been asked to make a recommendation about the version control system to use. The company consists in a currently small team of developers, ~16; and uses Microsoft Visual Studio 2008 to develop.

The company currently has a small SVN repository, which will need to be migrated to a new central repository, and local repositories.

At first I thought of going with Git, since I had heard good stuff about it. But, as I read more and more on the web, Git seems to be complicated and dangerous to use, so I am leaning more and more towards Mercurial.

The analysis of Google and the comments of 'werner.punz' confirm this.

Now, if you who is reading this and is well learned about Git and Mercurial, has anything useful to say, it is welcomed, and would be appreciated.

Thanks a lot!

::...
免责声明:
当前网页内容, 由 大妈 ZoomQuiet 使用工具: ScrapBook :: Firefox Extension 人工从互联网中收集并分享;
内容版权归原作者所有;
本人对内容的有效性/合法性不承担任何强制性责任.
若有不妥, 欢迎评注提醒:

或是邮件反馈可也:
askdama[AT]googlegroups.com



自怼圈/年番新

DU21.7
关于 ~ DebugUself with DAMA ;-)


关注公众号, 持续获得相关各种嗯哼:
zoomquiet


粤ICP备18025058号-1
公安备案号: 44049002000656 ...::