ADO – Migrating a Repository to Azure Repos (and keeping your commit history)

The most direct way to migrate a repo into Azure Repos is to create a new, blank repository. This may mean making a new project. From the organization’s main page, click “New project”

Or it may mean making a new repo in an existing project. From an existing repo, click the drown-down next to the repo name and select “New repository”

Name the repository but don’t add a README. We want a blank repo

Note the URL to the repository – in this case, it’s https://ado0255@dev.azure.com/ado0255/History%20Test/_git/Another%20History%20Test

Find the URL for your existing Git repo – if you cd into the project’s folder and run “git remote -v”, you will get a list of the repos. Make a new folder somewhere – this is a temporary staging area to move the data from your existing repo over to the new Azure Repo. Change directories into your new folder. Run git clone –mirror URLToOldRepo

You will see data being downloaded from your git server.

Change directories into the folder that just got downloaded. You won’t see your code like you normally do when you clone a git repo. You’re looking at the underlying git stuff that makes up the repo. You’re code is all in there, as are all of the branches and commit history.

Now add the new Azure Repo as a remote – in this case, I’m naming the remote “ado”. Then run “git push ado –all” to push everything up to the new Azure Repo.

Stuff will transfer – you may be prompted to log into your ADO repository first. Eventually, you’ll see new branches being created on the remote and the process will complete.

Refreshing the Azure Repo, you’ll now see the files.

Selecting “Commits” will display the commit history.

Anyone else using the repo will need to add the new remote. Use “git remote rm origin” to remove the existing origin, then use “git remote add origin url” to add the new Azure Repo as origin.

ADO – Cleaning up test repos and projects

I find the process to delete repositories and projects to be nonintuitive. Since I create a lot of projects and repos for testing and documentation, it’s nice to be able to clean them up when I’m done!

To delete an Azure Repo, navigate to a repo and select the drop-down next to the repo name. Select “Manage repositories”

With your mouse over a repository, there’s a hamburger menu at the right-hand side of the listing. Click it and select “Delete”

You’ll need to type the repository’s full name to activate the delete button.

To delete a project, go to the organization’s home page and select “Organization Settings” from the lower left-hand corner of the screen.

Select “Projects” from the left-hand navigation bar

With your mouse over the project listing, you’ll have a hamburger menu. Click it and select “Delete Project”

You’ll need to type the project name to activate the delete button.

 

Math Time — COVID Edition

Scott’s dad gets on our cases about being paranoid hypochondriacs (or whatever) because we’re still wearing masks and have Anya in online school for another year. The governor dropped the health orders, after all. Anya is too young to be vaccinated, but he’s safe … and kids don’t get sick anyway. Now, I don’t believe the latter two “facts” — kids do get sick, even if it’s less virulent. And I’ve never seen anything published that indicates vaccinated individuals don’t spread the virus. Just that they don’t feel unwell (which, in my mind, makes them more likely to spread it ’cause they don’t know they are sick … the Yankees having so many vaccinated people test positive sticks in my mind. They wouldn’t have known they were sick if it weren’t for what I assume is routine team-wide testing). And it’s difficult to explain to someone who has already made a decision … but the math just doesn’t support the “it’s all good” attitude people are adopting. I’m not an epidemiologist — I went to school for theoretical physics and work in computer science. I have done a lot of data mining and analysis, so I’ve got a decent understanding of the math side of epidemiology without any of the “so what do we do about it” medical knowledge. That being said … the math side of it can be helpful.

There’s a rate of spread for infections — computer viruses or human, in fact. There’s an initial rate of spread when no one has any immunity / has patched their computer (R0 to epidemiologist). If one person gets the virus, they give it to x people over the course of their infection. This is where you either see the total number of infected people trend toward zero of infinity — that is, if one infected person infects 0.5 (i.e. for every two infected people, you get one more person infected) … eventually the virus dies out. If one infected person infects ten others? This is a ever increasing progression — those ten each infect ten more for 100 infected people. Who each infect 10 for 1000 infected people. Which doesn’t seem bad — but those each infect 10 for 10,000 infected. Then 100,000. For each iteration, the number of infected people is 10^n — 10,000,000,000 is ten iterations down.

But preventative measures get taken — in one case, a computer virus caused my employer to shut down the LAN facing ports on every router in the company. Techs had to walk around with a fix-it CD, clean up every computer on a subnet, and then request the subnet be returned to the network. And, if we saw the virus propagating from that subnet? It got locked down again. Highly disruptive, but effective. And that’s where we were last spring with stay-at-home orders.

There are less severe precautions — computers have anti-virus software that look for virus-like activity for day zero identification. In human terms, that means we’re washing our hands after coming home from an outing. Or, as of last spring, wearing masks. Any of these precautions reduce the R0 value — but it can be difficult to predict exactly how much these actions will reduce the rate of spread.

Vaccines, on the other hand, have a quantified (and published) impact on spread. That efficacy and the percentage of the population that has been vaccinated scale the R0 value. The effective rate of spread is R0 * (1 – ( (vaccine efficacy) * (% of population that is vaccinated) ) ). If a vaccine prevents infection for half of the people who are exposed, then the effective rate of spread after vaccination is R0 * (1 – 0.5 * % of population that is vaccinated)). If a vaccine can prevent 90% of infections from occurring, the effective rate of spread after vaccination is R0 * (1 – 0.9 * % of population that is vaccinated)).

For convenience, I am going to ignore partially vaccinated individuals because I don’t know how effective a partial dose is at preventing transmission. The R0 published last year was around 3 — with about 40% of the population vaccinated with a 95% effective vaccine, that’s an effective rate of spread around 1.86 without other precautions being taken.

Now my numbers aren’t perfect — but this is almost a best-case effective rate of transmission. Another ten percent or so of the population is half-way vaccinated even if I don’t want to get that granular with my maths. But plenty of people got a 80-something percent effective vaccine, too. And the efficacy of each vaccine is reduced against variants. Having an effective transmission rate hovering around 2 seems, to me, like a premature time to cease taking other precautions.

County Building Department

It strikes me, every time I talk to someone from the auditor’s office or the building department, that county officials must talk to a lot of people after-the-fact … like they built a shed, someone noticed it, and now they’re going through the permitting process for that shed. Because they always seem surprised that I’m in the planning stages of a project and am ringing them up to make sure I’m doing all the right things in the right order.

My note-to-self for the day — while the Medina County Building Department does permit fences over 6′, they do not require anything for agricultural buildings and fences. If you’ve got an agricultural exemption from the Township for a building, they’ll happily agree that the fence around / next to that building is for agricultural use as well. (For non-agricultural fences, you fill out the residential building form and specify the perimeter of the fence for the sq ft area and not the square footage enclosed by the fence).

Thus, I’ve concluded that the steps to build a bigger chicken coop and a pasture are:

  1. Submit the agricultural exemption form to the township
  2. Once it is approved, e-mail a copy to the Medina County Building Department for their records (so when someone rings them up about some construction that doesn’t look like it should be there, the don’t have to waste a day driving out to look at a chicken coop)
  3. Build it

Our coop and greenhouse shouldn’t need a permit from the county because the size is under 200 sq ft.

Fence Options

We’re planning out a pasture for the chickens and turkeys — mostly back in the woods so they’ve got plenty of cover, lots of tree detritus to scratch at, and shade on these hot summer days. There are plenty of options for fencing — and a big jump in prices between 5′ and 6′ high fences. We’re thinking about overlapping two rolls of fence to create a cheaper version of 6+ foot high fencing — it’s $120 for two 3′ rolls v/s $160 for a single 6′ roll. Even a 3′ and a 4′ roll, creating a higher fence with maybe 6″ of overlap, is $140. Securing the two pieces along the entire fence line will be increased effort, but rolling out 6′ of fencing seems like it might be a lot of effort compared to rolling out a 4′ and a 3′ roll.

The hazard of curbside pickup

I’ve had an ongoing problem with the curbside grocery pickup — it’s a great time saver (and basically eliminates off-list purchasing / impulse shopping), and I get the limitations of asking someone else to think for you (I add a note that indicates the exact number of large/small *bunches* of bananas I actually want whilst ordering unit “1” banana, and I know they are not just going to figure it out and get jicama when they’re out of celery root). My problem is that the quality of the food that ends up in my order is absolutely not something I’d have bought. I won’t buy mushy, rotten oranges. Or visibly moldy strawberries. Or meat with huge chunks of fat. I’ve taken to putting in a big order of shelf-stable staples for pickup a few times a year and selecting the perishables myself. And accepting the time / quality trade-off when I ask them to select my produce for me.

Thought that was a problem unique to the grocery store, but we picked up chicken chow from Tractor Supply today. Last time, the 6 pound bag that was brought out instead of a 40 pound bag was really obvious. And quickly corrected. This time? We bought two bags of food — the first one was fine. Upon lifting the first bag out of the car, though, I discovered the second bag. A bag which looks like it was dragged across the warehouse floor and then taped up to retain whatever was left in the bag. This certainly isn’t a bag someone would have put into their cart at full price. I’ve seen other companies slap a discount sticker on a mostly full taped up bag. Unloading the ripped bag of something close to 40 pounds of chicken food, at full price, on a curbside pickup? Kinda sucky!

Luckily, Tractor Supply has excellent customer service policies. I used the ‘contact us’ form to tell them about the problem — and, yeah, I could have just brought it back to the store. But … had I been in the car park when I noticed the bag, I certainly would have asked for the bag to be swapped out. A couple extra minutes was worth it to avoid whatever contaminants got into the bag and whatever amount of food fell out of the bag. Half an hour of driving? Not worth it for what’s probably some warehouse dirt and maybe a pound of missing food. Within a few hours, a customer service rep rang be to apologize for the mangled bag. He refunded the purchase price and issued a gift card for our inconvenience. That’s more than fair.

Git – Removing Confidential Info From History

The first cut of code may contain … not best practice code. Sometimes this is just hard coding something you’ll want to compute / look up in the future. Hard coding user input isn’t a problem if my first cut is always searching for ABC123. Hard coding the system creds? Not good. You sort that before you actually deploy the code. But some old iteration of the file has MyP2s5w0rD sitting right there in plain text. That’s bad in a system that maintains file history! The quick/easy way to clean up passwords stashed within the history is to download the BFG JAR file.

For this test, I created a new repository in .\source then created three clones of the repo (.\clone1, .\clone2, and .\clone3). In each cloneX copy, I created a tX folder that has a file named ldapAuthTest.py — a file that contains a statically assigned password as

strSystemAccountPass = "MyP2s5w0rD"

The first thing I did was to redact the password in the files — this means anyone looking at HEAD won’t see the password. Source, clone1, and clone2 are all current. The clone3 copy has pulled all changes but has a local change committed but not merged.

To clean the password from the git history, first create a backup of your repo (just in case!). Then mirror the repo to work on it

mkdir mirror
cd mirror
git clone --mirror d:\git\testFilterBranch\source

 

Create file .\replacements.txt with the string to be redacted — in this case:

strSystemAccountPass = "MyP2s5w0rD"

Formatting notes for replacements.txt

MyP2s5w0rD # Replaces string with default ***REMOVED***
MyP2s4w0rD==>REDACTED # Replaces string using custom string REDACTED
MyP2s3w0rD==> # Replaces string with null -- i.e. removes the string
regex:strSystemAccountPass\s?=\s?"MyP2s2w0rD""==>REDACTED # Uses a regex match -- in this case we may or may not have a space around the equal sign

So, in my mirror folder, I have the replacement.txt file which defines which strings are replaced. I have a folder that contains the mirror of my repo.

lisa@FLEX3 /cygdrive/d/git/testFilterBranch/mirror
$ ls
replacements.txt source.git

To replace my “stuff”, run bfg using the –replace-text option. Because I only want to replace the text in files named ldapAuthTest.py, I also added the -fi option

java -jar ../bfg-1.14.0.jar --replace-text ..\replacements.txt -fi ldapAuthTest.py source.git

 

lisa@FLEX3 /cygdrive/d/git/testFilterBranch/mirror
$ java -jar ../bfg-1.14.0.jar --replace-text replacements.txt -fi ldapAuthTest.py source.git

Using repo : D:\git\testFilterBranch\mirror\source.git

Found 3 objects to protect
Found 2 commit-pointing refs : HEAD, refs/heads/master

Protected commits
-----------------
These are your protected commits, and so their contents will NOT be altered:
* commit 87f1b398 (protected by 'HEAD')

Cleaning
--------
Found 5 commits
Cleaning commits: 100% (5/5)
Cleaning commits completed in 613 ms.

Updating 1 Ref
--------------

Ref Before After
---------------------------------------
refs/heads/master | 87f1b398 | 919c8f0f

Updating references: 100% (1/1)
...Ref update completed in 151 ms.

Commit Tree-Dirt History
------------------------

Earliest Latest
| |
. D D D m

D = dirty commits (file tree fixed)
m = modified commits (commit message or parents changed)
. = clean commits (no changes to file tree)

Before After
-------------------------------------------
First modified commit | dc2cd935 | 8764f6f1
Last dirty commit | 9665c4e0 | ccdf0359

Changed files
-------------

Filename Before & After
-------------------------------------
ldapAuthTest.py | 25e79fa6 ? 4d12fdad

In total, 8 object ids were changed. Full details are logged here:
D:\git\testFilterBranch\mirror\source.git.bfg-report\2021-06-23\12-50-00

BFG run is complete! When ready, run: git reflog expire --expire=now --all && git gc --prune=now --aggressive

Check to make sure nothing looks abjectly wrong. Assuming the repo is sound, we’re ready to clean up and push these changes.

cd source.git

git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push

 

Pulling the update from my source repo, I have merge conflicts

These are readily resolved and the source repo can be merged into my local copy.

And the change I had committed but not pushed is still there.

Pushing that change produces no errors

Now … pushing the bfg changes may not work. In my case, the real repo has a bunch of branchs and I am getting “non fast-forward merges”. To get the history changed, I need to do a force push. Not so good for the other developers! In that case, everyone should get their changes committed and pushed. The servers should be checked to ensure they are up to date. Then the force push can be done and everyone can pull the new “good” data (which, really, shouldn’t differ from the old data … it’s just the history that is being tweaked).

DNF — What Provides This File?

“Dependency hell” used to be a big problem — you’d download one package, attempt to install it, and find out you needed three other packages. Download one of them, attempt to install it, and learn about five other packages. Fifty seven packages later, you forgot what you were trying to install in the first place and went home. Or, I suppose, you managed to install that first package and actually use it. The advent of repo-based deployments — where dependencies can be resolved and automatically downloaded — has mostly eliminated dependency hell. But, occasionally, you’ll have a manual install that says “oh, I cannot complete. I need libgdkglext-x11-1.0.so.0 or libminizip.so.1 … and, if there’s a package that’s named libgdkglext-x11 or libminizip … you’re good. There’s not. Fortunately, you can use “dnf provides” to search for a package that provides a specific file — thus learning that you need the gtkglext-libs and minizip-compat packages to resolve your dependencies.

Zoning, Conditional Use, and the Comprehensive Plan

Long blurb I’ve written in response to the public outcry about the Hinckley Township BZA approving a conditional use permit for Pride One to construct senior apartment housing in a business district:

Since ‘senior housing’ is a conditional use in business districts, my opinion is that our zoning resolution put the BZA in a bind where they *had* to approve the request. When the BZA rule on something, decisions can be appealed in court. Now, if you wanted to build a shed a few feet into a riparian setback and they said no, you probably wouldn’t blow a couple grand on a lawyer to convince a Medina County Court of Appeals judge that the BZA were wrong. Big companies, on the other hand, will absolutely do that. Almost every case I see in the court docket is <Some Company> v <Some Township’s BZA>.

The setback variances, as an example, the company never presented any good justification for *needing* the variance. They wanted to build more stuff and ‘needed’ to build where they weren’t allowed to in order to have more units to rent. “I want to do it” isn’t the bar you’ve got to pass when requesting a variance (otherwise zoning rules would be pointless!). Had they taken appealed the denial in court, I expect the BZA’s decision would have stood. There are cases where the court determined that you don’t have a right to the *best* use of your property — in this case, they don’t have a right to pack as many units in the lot as possible.

The conditional use approval? If it had been denied, Pride One would likely have won an appeal (and quickly). We’d have the facility right where they wanted it, and they could have gone after the township for “damages” — legal fees, lost business, etc. Why? Because ‘senior housing’ is specifically listed as a conditional use for business districts. Our township comprehensive plan says residents *want* senior housing so long-time residents who no longer want to care for two acres don’t have to move away from the township. It’s not like we’ve got five other senior housing facilities that have already more than sufficiently addressed this need. There’s nothing in our zoning or comprehensive plan that says we want to slow the creation of senior housing facilities. They’ve got a lot of evidence that their senior housing facility should be allowed. I cannot think of any good (i.e. would stand up in court) reason the BZA could have given for denying the conditional use.

Which, of course, begs the question ‘why are we allowing senior housing in business districts!?!’ … and that, it seems, is the result of a comprehensive plan that got feedback from some 6% of the residents back in 2014 or 2015. Why would someone say “ok, that’s a reasonable response rate … I’m going to write up what the township wants based on these responses” is a great question. It seemed, from the past few Trustee meetings, that it’s possible to revise the comprehensive plan earlier than planned. That would be the first step in avoiding this in the future — once the plan changes, it’s reasonable to revise the zoning resolution to address the newly stated desires of the township. And, if there is an update to the master plan, make sure you respond to whatever survey is used. Make sure your friends who live in the township respond. Make sure your neighbors respond. Claiming to develop a plan based on responses from a small sampling of residents is absurd. Be specific — if you think we need more senior housing but don’t want apartments, then specify single-family residential senior housing.

I’d encourage people to read through the zoning resolution (https://www.hinckleytwp.org/content/zoning-regulations) and identify anything *else* that they don’t like. Look at the full list of conditional uses — you don’t want a hospital, then hospital shouldn’t be a conditional use either. Instead of reacting after the fact, we need to push through changes *before* whatever-it-is starts being built. Existing stuff gets grandfathered in (i.e. the senior housing is there even if we change the regulations), but new construction is limited by the new rules. Read the comprehensive plan (https://www.hinckleytwp.org/content/comprehensive-plan-master-policy-plan) and see if (1) it accurately reflects what you want to see in the community going forward and (2) if you think that plan is reasonably addressed by the zoning resolution.

Get engaged — attend township meetings (we’re starting to record the meetings and posting them on YouTube for people who aren’t able to join meetings at the scheduled time — https://www.youtube.com/channel/UCTffOEhdDc7UO0fN-tgyEnw) or read the minutes. Email the trustees to let them know what you think. Do you think the comprehensive plan needs to be updated? Should there be a minimum response rate to consider feedback to be representative of the community’s desires? Maybe you think the Township should explore adding a growth management plan — limiting the number of building permits available. Hudson did so to ensure the City didn’t grow faster than the capabilities of city services (police, fire). Maybe you think the lot size or setbacks need to be increased. Maybe you think everything is awesome the way it is. Whatever you think, make sure your voice is heard.

Keeping Hinckley Rural – Zoning

What do I think might help keep the township rural? I think we need a combination of changes to the zoning resolution, training for BZA members (possibly even replacing BZA members), and changes to how variances are written.

Zoning changes — Increasing lot size has been discussed, and doing so would certainly would reduce the amount of development. We might want to include minimum green space / maximum total lot coverage in the zoning regulations. Some townships out in Geauga County have looked at such regulations to retain rural character. As currently written, I believe I could transform the entirety of my 2 acre lot into driveway, parking lot, patio, etc. Or just pave the whole thing. Increasing minimum lot widths at the road might ensure homes are placed farther apart, although that might have the unintended consequence of moving some houses closer to the street with neighboring ‘flag’ lots in the rear. Increasing the side yard setbacks would ensure spacing between homes too. Including a slow growth plan — limiting the number of new construction permits per year — could ensure development doesn’t outpace the township’s ability to provide police, fire, and road repair services.

BZA Training — The BZA does not have legal authority to issue variances any time someone finds a zoning regulation inconvenient. Their authority is to issue a variance when strict adherence to zoning regulations deprives the owner of beneficial use of their property. An extreme example — a lot with a bunch of riparian setbacks that mean the buildable area is a 2×40 rectangle toward the front edge of the property. The BZA has the authority to approve a variance from the township’s fairly substantial front yard setback because a 2×40 buildable area has certainly deprived the owner of the ability to have a house on their residential property. Building a house forty feet into the front yard setback lets them have a 40×42 house that’s closer to the road than the zoning regulations stipulate. Building within a riparian setback can destabilize the bank of the stream, so isn’t a good option.

There are several variances where I don’t believe the requestor has shown a “practical difficulty” for area variances. These requests basically amounted to “yeah, I could do it another way … but I don’t want to”. The BZA does not have the legal authority to issue a variance, and I believe these variances would be overturned if challenged in court. That’s a whole process — and you’ve got to own one of the adjoining properties to have standing to challenge it in court. But there’s no reason the BZA should be exceeding its authority in approving variances, and it seems like they need to have what does and does not constitute a practical difficulty or hardship clarified.

Changes to how variances are written — I think variances should be written as restrictively as possible to address the specific problem presented to the Board. If my rear property line is 200′ long, and I want to build a 20×20 garage in the rear yard setback because of some Very Good Reasons, there is no reason for a variance of 20′ from the 50′ rear yard setback. Issuing a non-specific variance means additional, future, construction can also be built 20′ into the rear yard setback. Without having to show any difficulty in building the structure elsewhere — you’ve basically got a lot with a special rear yard setback instead of special permission to build that garage. Write a variance allowing 20′ at the rear of the property to have a 20′ variance from the 50′ rear yard setback. Write a variance allowing the building blueprints presented to the Board to have a 20′ variance.