TOPIC: GIT
Vibe Coding, AI App Builders and the Changing Shape of Software Creation
A distinct cluster of digital tools has been forming around software creation, and it does not fit especially neatly into older categories. Some of these products began as developer infrastructure, some as online coding environments, and some as AI-powered builders for people with little or no conventional programming background. Increasingly, though, they are converging around a shared promise: describe what you want in ordinary language, let the system generate much of the software, and refine the result through an iterative back-and-forth.
That convergence is why platforms such as Vercel, v0, Replit, Bolt.new and Lovable are often mentioned together even though they did not begin in the same place. In older taxonomies, one might have sat under hosting, another under browser-based coding and another under no-code or low-code creation. With AI now sitting closer to the centre of each experience, the boundaries are less tidy, and what emerges instead is a broader ecosystem of AI-assisted application creation, one that affects how software is built, who can build it and what people mean when they talk about coding in the first place.
The Term That Named the Movement
Before examining the individual platforms, it is worth understanding where the phrase "vibe coding" came from, since it now frames so much of the conversation around these tools. The term was coined by AI researcher Andrej Karpathy in a post on X (formerly Twitter) on 2nd February 2025. He described it as a style of building where you fully give in to the process, embrace rapid iteration and let the AI handle the details of implementation, to the point of forgetting that code even exists underneath. The phrase spread rapidly, and by the end of 2025, Collins Dictionary had named it their Word of the Year for 2025, a recognition of just how thoroughly the idea had entered mainstream discourse.
Karpathy's framing was originally casual and deliberate in its provocation. He was describing the experience of using large language models to build hobby projects by intent and iteration rather than by carefully planned, line-by-line implementation. The term has since broadened considerably, and in some engineering circles it has taken on more cautious connotations when applied to production systems. Even so, it remains the most widely understood shorthand for this style of prompt-driven development, and it shapes how the platforms below are discussed and marketed.
Vercel and Next.js
At one end of this landscape sits Vercel, which still fits most cleanly under software development tools enhanced by AI. Its core identity remains tied to deployment, hosting and developer workflow tooling rather than to frontier model development or general-purpose AI assistance. Next.js, the popular full-stack React framework, is maintained by Vercel, and many modern AI web applications are built with it and deployed on the Vercel platform. This overlap with companies such as OpenAI, Anthropic and Replicate helps explain why Vercel can appear closer to the AI conversation than a traditional hosting platform might once have done.
Even so, Vercel is not best understood as an AI assistant or a research platform in its own right. It remains primarily infrastructure and deployment, with growing AI-related features around the edges. The company promotes AI SDKs and tooling for building chatbots and AI interfaces, but that still serves the broader purpose of helping teams develop and ship applications, rather than replacing that process with a standalone AI service.
v0 by Vercel
The picture changes when v0 enters the discussion, and it began as a form of generative UI, focused on AI-generated React and Next.js interfaces and on rapid frontend prototyping. In that earlier form, it looked like a useful but relatively bounded addition to Vercel's existing developer ecosystem. The product launched in beta in October 2023, and by January 2026 it had rebranded from v0.dev to v0.app, with over six million developers using the platform by that point. More recently, it has evolved into something broader, including full-stack app generation, website generation, agentic coding workflows, GitHub integration, deployment automation and increasingly autonomous software development.
That makes the Vercel ecosystem easier to understand when its parts are considered separately. Vercel handles hosting, deployment and infrastructure, while Next.js is the web framework that underpins much of the work produced there, and v0 sits on top of both as the AI-driven generation layer where interfaces, applications and workflows can increasingly be created from natural-language prompts. Seen this way, it becomes clearer why people now mention Vercel not only alongside hosting platforms such as Netlify or Cloudflare Pages, but also alongside browser-based tools such as Lovable, Replit and Bolt.new. v0 has moved into the same general current as vibe coding, where natural-language intent drives substantial code generation and rapid iteration. A significant rebuild in February 2026, framed by Vercel itself as tackling the gap between prototype and production, added enterprise-grade security controls and tighter integration with existing codebases, an acknowledgement that the earlier version's generated code, while popular, was often unsuitable for real deployment without considerable rework.
Replit
Replit occupies a more ambiguous but equally revealing position. It is an online programming and app development platform that runs entirely in the browser, and that basic fact explains much of its appeal. Traditional local development often requires installing languages, configuring environments, managing dependencies and arranging deployment separately. Replit reduces much of that friction by allowing someone to open a browser tab, create a project and start coding immediately. The platform supports over 50 programming languages, with Python and JavaScript among the most widely used, and also covers TypeScript, C, C++, Go, Rust, Java and PHP, among many others.
In its earlier form, Replit was widely understood as an educational coding environment and a convenient cloud-based place to experiment with code. It was founded in 2016 by Amjad Masad with the stated aim of making programming as accessible as Google Docs. Over time, it grew into something closer to a cloud development platform, and more recently AI-assisted software development has become central to its public identity. Where it once offered a blank editor in the browser, it now guides users from a plain-English description of an app through generated starter code, interactive refinement and on to hosting, all without leaving the platform. AI code completion, debugging assistance and automated environment setup are part of that journey, as are agent-like workflows capable of building or modifying entire projects.
An All-in-One Character
That all-in-one character is what makes Replit distinct. Rather than asking a developer to stitch together a separate editor, runtime, host and collaboration tool, it folds all of those functions into a single browser-based environment, with AI coding assistance built in throughout. It overlaps in part with GitHub Codespaces, CodeSandbox and Lovable among browser-based environments, yet it differs from each in emphasis. Compared with Vercel, Replit feels much closer to an AI-native development environment than to deployment infrastructure, and compared with a conventional online editor, it pushes further towards autonomous generation and guided building.
That quality is important because Replit is often described in terms such as vibe coding platform, AI-native IDE or browser-based autonomous coding environment. Those descriptions point to a shift in the role of the developer. Rather than beginning with a blank file and writing everything line by line, a user may instead begin with a description, inspect what appears, correct it and continue in conversation with the system. The coding has not disappeared, but the interface to coding has changed significantly. The degree of autonomy that makes this possible also carries risk, as demonstrated in July 2025 when Replit's AI agent deleted the entire production database of SaaStr, a community for software business founders, during a test run, having ignored explicit instructions to freeze code changes, and subsequently attempted to conceal the damage by generating thousands of fake records. Replit's CEO apologised publicly, and the company introduced additional safeguards, but the incident drew widespread attention to the question of how much autonomous action is safe to delegate to an AI agent operating on live systems.
Bolt.new
Bolt.new pushes further along that spectrum, but arrives there from an unusual direction. Where Replit's move towards AI-assisted creation was a gradual evolution of an existing development platform, Bolt.new was built from the outset around a proprietary technology called WebContainers, developed by its parent company StackBlitz over the course of several years. StackBlitz was founded in 2017 by Eric Simons and Albert Pai with the aim of moving web development entirely into the browser, and WebContainers is the fruit of that work: a micro-operating system that runs Node.js and related tooling natively inside a browser tab using WebAssembly, with no remote server involved. When Bolt.new launched in October 2024, it combined that runtime with large language model code generation, and the result was something that could not only write code in response to a prompt but immediately execute it in the same environment and verify the output before the user had noticed a problem.
That feedback loop is what distinguishes Bolt.new most sharply from tools that generate code and hand it back for the user to run elsewhere. Because the code executes locally in the browser as it is produced, Bolt.new can catch errors, attempt fixes and iterate without the round-trip delay of cloud-based environments. The product launched initially using Anthropic's Claude 3.5 Sonnet as its underlying model, and StackBlitz became an official Anthropic partner in June 2025, opening access to the full range of Claude models. The growth that followed the October 2024 launch was striking: the product went from zero to four million dollars in annualised recurring revenue within its first thirty days, and reached forty million dollars ARR within five months, a trajectory that drew comparisons to the early growth of ChatGPT.
The platform has continued to develop since that launch. A significant update released in October 2025 added Bolt Cloud, bringing built-in databases, authentication, file storage and hosting to a product that had previously relied on external services such as Netlify and Supabase for those functions. Integrations with Stripe for payments, Figma for design import and GitHub for version control are also available, and the platform accepts inputs as text, images and Figma files as well as plain prompts. It exposes the code it generates, allows direct editing inside a browser IDE and gives users enough visibility to understand what has been built, which keeps it closer to the developer end of the spectrum than what comes next.
Lovable
Lovable sits the furthest along that spectrum. It is an AI-powered app builder that focuses more strongly on natural-language software creation than either Replit or Bolt.new does. Where those platforms still feel recognisably like coding environments, giving users access to the code being produced and expecting some degree of technical engagement, Lovable comes across more as an AI product generator. The central idea is not so much to provide a development environment with AI assistance as to let a person describe the application they want and have the system build a substantial first version on their behalf.
In practical terms, that means users can enter prompts such as a request for a travel blog with dark mode, a dashboard for train delays or a booking system for hiking tours. Lovable then generates frontend UI, layouts, components, database structure and often backend integrations. It started life as GPT Engineer, an open-source project, before launching commercially as Lovable in November 2024. In December 2025, it closed a $330 million Series B round at a $6.6 billion valuation, with enterprise customers including Klarna, Uber and Zendesk. This orientation makes it especially relevant for rapid prototyping and attractive to founders, designers, hobbyists and other non-traditional developers.
For that reason, Lovable belongs more naturally in conversations about agentic AI options than in discussions of conventional software development platforms. It is not a frontier model provider, a research tool or a traditional developer platform in the older sense. Instead, it forms part of a wider movement towards AI-generated applications, low-code and no-code tooling and what might be called software by conversation. The trade-off that comes with that approach became visible in April 2026, when a security researcher disclosed a broken access control vulnerability that had allowed unauthorised users to read the source code, database credentials and AI chat history of projects created before November 2025. Employees from major technology companies were among those with affected accounts, and the flaw had been reported to Lovable 48 days before it was made public. The incident underlined that the speed and abstraction that make these tools attractive do not remove the need for the security discipline that production software has always required.
Overlapping but Not Interchangeable
Taken together, these platforms show that the old boundaries between infrastructure, coding environments and app generators are becoming less stable. Each of them has moved, to varying degrees, in the same direction: towards natural-language input, generated output and a reduced expectation that the person building software will write every line of it themselves. The overlap among them is not accidental, and the fact that a hosting company, a browser IDE and an AI app builder are now discussed in the same breath reflects a broader shift in what software tooling is understood to be.
For readers trying to make sense of the current landscape, the simplest framing may be that these are AI-native or AI-assisted software development platforms arranged along a spectrum from infrastructure to conversation. At one end, Vercel and v0 together span the distance from deployment layer to AI-led generation, with the latter having pulled the whole ecosystem into a discussion it would not have joined a few years ago. Replit and Bolt.new occupy the middle ground, both giving users visibility into the code being produced, but Replit through the depth and flexibility of a full development environment and Bolt.new through the speed and self-contained nature of its browser-native runtime. At the far end, Lovable treats generation as its starting point rather than a feature layered onto something else, and makes the least demand on the person building to understand what is happening underneath.
Accessibility, Complexity and the Limits of Generation
This shift has implications beyond product positioning. One of the most obvious is accessibility. Tools that can generate starter applications, configure environments and handle deployment lower some of the barriers that previously kept software creation inside narrower technical circles. A person who would once have been stopped by installation issues, tooling complexity or lack of confidence with syntax may now get much further, though that does not mean expertise has become irrelevant; it means only that the route into creating software has changed and, in some cases, widened.
The harder question is what happens when those generated applications are expected to do something more than demonstrate a concept. The gap between a working prototype and a production system has always existed, but vibe coding has sharpened the surrounding debate considerably. In a December 2025 controlled study by security firm Tenzai, fifteen identical web applications were built using five AI coding agents, and the findings were pointed: across all fifteen applications, not one had CSRF protection and not one set standard security headers. Every application that included a URL-handling feature introduced a server-side request forgery vulnerability. Separately, research from 2025 found that AI-assisted code commits introduced hardcoded credentials at roughly twice the rate of human-only code, a pattern that has contributed to a significant rise in leaked API keys and secrets in public repositories.
Security is the sharpest edge of the criticism, but it is not the only one. Studies of AI-generated codebases have found that technical debt accumulates substantially faster than in traditionally engineered software, and that the absence of consistent architectural decisions, which a human team would establish and revisit over time, makes codebases harder to extend and maintain as they grow. An AI model has no memory of the patterns agreed upon in a previous session, and the context window has limits on how much of a large codebase it can hold in view at once. The result, as the software grows, can be inconsistency that is expensive to untangle. An August 2025 survey of eighteen CTOs by Final Round AI found that sixteen had experienced production problems they attributed directly to AI-generated code, and the consistent concern was not that AI tools were useless but that teams were using them without the engineering oversight that production software demands.
There is also a subtler, longer-term concern about the pipeline of people with the skills to address these problems. LeadDev's AI Impact Report 2025 found that 54% of engineering leaders expected junior developer hiring to decrease as a direct result of AI coding tools. The difficulty is that debugging, code review and architectural reasoning are skills that developers have traditionally built precisely by doing the lower-level work that AI is now absorbing. If fewer people develop those skills, the question of who fixes the AI-generated problems at scale becomes harder to answer. That tension helps explain why this area deserves to be treated as a topic in its own right, rather than squeezed into pre-existing categories. These platforms are reshaping the workflow of application creation itself, and the full consequences of that reshaping, for security, maintainability and the development of engineering skill, are still working themselves out.
What the Shift in Software Creation Actually Means
As this approach continues to develop, the most useful way to understand it may be not through rigid labels but through the changing relationship between people, code and tools. Software creation is becoming less linear and more conversational, and the path from idea to prototype is shortening. The distinction between writing code, directing a system to write code and assembling generated parts is becoming less clear. The vibe coding idea, coined in a single social media post in early 2025 and quickly adopted as a word of the year, has given this moment a name that captures both its appeal and its informality. Whether these platforms collectively represent a temporary shift in tooling or something more fundamental about who gets to build software will become clearer only as the generation of applications they enable moves from demonstration into sustained, real-world use.
Lessons learned during migrations to Grav CMS from Textpattern
After the most of four years since the arrival of Textpattern 4.8.8, Textpattern 4.9.0 was released. Since, I was running two subsites using the software, I tried one of them out with the new version. That broke an elderly navigation plugin that no longer appears in any repository, prompting a rollback, which was successful. Even so, it stirred some curiosity about alternatives to this veteran of the content management system world, which is pushing on for twenty-two years of age. That might have been just as well, given the subsequent release of Textpattern 4.9.1 because of two reported security issues, one of which affecting all preceding versions of the software.
Well before that came to light, there had been a chat session in a Gemini app on a mobile which travelling on a bus. This started with a simple question about alternatives to Textpattern. The ensuing interaction led me to choose Grav CMS after one other option turned out to involve a subscription charge; A personal website side hustle generating no revenue was not going to become a more expensive sideline than it already was, the same reasoning that stops me paying for WordPress plugins.
Exploring Known Options
Without any recourse to AI capability, I already had options. While WordPress was one of those that was well known to me, the organisation of the website was such that it would be challenging to get everything placed under one instance and I never got around to exploring the multisite capability in much depth. Either way, it would prove to involve quite an amount of restructuring, Even having multiple instances would mean an added amount of maintenance, though I do automate things heavily. The number of attack surfaces because of database dependence is another matter.
In the past, I have been a user of Drupal, though its complexity and the steepness of the associated learning curve meant that I never exploited it fully. Since those were pre-AI days, I wonder how things would differ now. Nevertheless, the need to make parts of a website fit in with each other was another challenge that I failed to overcome in those days. Thus, this content framework approach was not one that I wished to use again. In short, this is an enterprise-grade tool that may be above the level of personal web tinkering, and I never did use its capabilities to their full extent.
The move away from Drupal brought me to Hugo around four years ago. That too presents a learning curve, though its inherent flexibility meant that I could do anything that I want with it once I navigated its documentation and ironed out oversights using web engine searches. This static website generator is what rebuilds a public transport website, a history website comprised of writings by my late father together with a website for my freelancing company. There is no database involved, and you can avoid having any dynamic content creation machinery on the web servers too. Using Git, it is possible to facilitate content publishing from anywhere as well.
Why Grav?
Of the lot, Hugo had a lot going for it. The inherent flexibility would not obstruct getting things to fit with a website wide consistency of appearance, and there is nothing to stop one structuring things how they wanted. However, Grav has one key selling point in comparison: straightforward remote production of content without recourse to local builds being uploaded to a web server. That decouples things from needing one to propagate the build machinery across different locations.
Like Hugo, Grav had an active project behind it and a decent supply of plugins and an architecture that bested Textpattern and its usual languid release cycle. The similarity also extended as far as not having to buy into someone else's theme: any theming can be done from scratch for consistency of appearance across different parts of a website. In Grav's case, that means using the Twig PHP templating engine, another thing to learn and reminiscent of Textpattern's Textile as much as what Hugo itself has.
The centricity of Markdown files was another area of commonality, albeit with remote editing. If you are conversant with page files having a combination of YAML front matter and subsequent page content from Hugo, Grav will not seem so alien to you, even if it has a web interface for editing that front matter. This could help if you need to edit the files directly for any reason.
That is never to say that there were no things to learn, for there was plenty of that. For example, it has its own way of setting up modular pages, an idea that I was to retrofit back into a Hugo website afterwards. This means care with module naming as well as caching, editor choice and content collections, each with their own uniqueness that rewards some prior reading. A learning journey was in the offing, a not attractive part of the experience in any event.
Considerations
There have been a number of other articles published here regarding the major lessons learned during the transitions from Textpattern to Grav. Unlike previous experiences with Hugo, another part of this learning was the use of AI as part of any debugging. At times, there was a need to take things step by step, interacting with the AI instead of trying out a script that it had put my way. There are times when one's own context window gets overwhelmed by the flow of text, meaning that such behaviour needs to be taken in hand.
Another thing to watch is that human consultation of the official documentation is not neglected in a quest for speed that lets the AI do that for you; after all, this machinery is fallible; nothing we ever bring into being is without its flaws. Grav itself also comes from a human enterprise that usefully includes its own Discord community. The GitHub repository was not something to which I had recourse, even if the Admin plugin interface has prompts for reporting issues on there. Here, I provide a synopsis of the points to watch that may add to the help provided elsewhere.
Choosing an Editor
By default, Grav Admin uses CodeMirror as its content editor. While CodeMirror is well-suited to editing code, offering syntax highlighting, bracket matching and multiple cursors, it renders its editing surface in a way that standard browser extension APIs cannot reach. Grammar checkers and spell-check extensions such as LanguageTool rely on native editable elements to detect text, and CodeMirror does not use these. The result is that browser-based writing tools produce no output in Grav Admin at all, which is a confirmed architectural incompatibility rather than a configuration issue documented in the LanguageTool issue tracker.
This can be addressed by replacing CodeMirror using the TinyMCE Editor Integration plugin, installable directly from the Admin plugin interface, which brings a familiar style of editor that browser extensions can access normally. Thus, LanguageTool functionality is restored, the writing workflow stays inside Grav Admin and the change requires only a small amount of configuration to prevent TinyMCE from interfering with Markdown files in undesirable ways. Before coming across the TinyMCE Editor plugin, I was seriously toying with the local editing option centred around a Git-based workflow. Here, using VS Code with the LanguageTool extension like I do for Hugo websites remained a strong possibility. The plugin means that the need to do this is not as pressing as it otherwise might be.
None of this appears to edit Twig templates and other configuration files unless one makes use of the Editor plugin. My brief dalliance with this revealed a clunky interface and interference with the appearance of the website, something that I never appreciated when I saw it with Drupal. Thus, the plugin was quickly removed, and I do not miss it. As it happened, editing and creating files over an SSH connection with a lightweight terminal editor worked well enough for me during the setup phase anyway. If I wanted a nicer editing experience, then a Git-based approach would allow local editing in VSCode before pushing the files back onto the server.
Grav Caching
Unlike WordPress, which requires plugins to do so, Grav maintains its own internal cache for compiled pages and assets. Learning to work with it is part of understanding the platform: changes to CSS, JavaScript and other static assets are served from this cache until it is refreshed. That can be accomplished using the admin panel or by removing the contents of the cache directory directly. Once this becomes second nature, it adds very little overhead to the development process.
Modular Pages
On one of the Textpattern subsites, I had set up the landing page in a modular fashion. This carried over to Grav, which has its own way of handling modular pages. There, the modular page system assembles a single page from the files found within a collection of child folders, each presenting a self-contained content block with its own folder, Markdown file and template.
All modules render together under a single URL; they are non-routable, meaning visitors cannot access them directly. When the parent folder contains a modular.md file, the name tells Grav to use the modular.html.twig template and whose front matter defines which modules to include and in what order.
Module folders are identified by an underscore at the start of their name, and numeric prefixes control the display sequence. The prefix must come before the underscore: _01.main is the correct form. For a home page with many sections this structure scales naturally, with folder names such as 01._title, 04._ireland or 13._practicalities-inspiration making the page architecture immediately readable from the file system alone.
Each module's Markdown filename determines which template renders it: a file named text.md looks for text.html.twig in the theme's modular templates folder. The parent modular.md assembles the modules using @self.modular to collect them, with a custom order list giving precise control over sequence. Once the folder naming convention and the template matching relationship are clear, the system is very workable.
Building Navigation
Given that the original impetus for leaving Textpattern was a broken navigation plugin, ensuring that Grav could replicate the required menu behaviour was a matter of some importance. Grav takes a different approach to navigation from database-driven systems, deriving its menu structure directly from the content directory tree using folder naming conventions and front matter flags rather than a dedicated menu editor.
Top-level navigation is driven by numerically prefixed subfolders within the content directory (pages), so a structure such as 01.home, 02.about and 03.blog yields an ordered working menu automatically. Visibility can be fine-tuned without renaming folders by setting visible: true or visible: false in a page's YAML front matter, and menu labels can be shortened for navigation purposes using the menu: field while retaining a fuller title for the page itself.
The primary navigation loop iterates over the visible children of the pages tree and uses the active and activeChild flags on each page object to highlight the current location, whether the visitor is on a given top-level page directly or somewhere within its subtree. A secondary menu for the current section is assembled by first identifying the active top-level page and then rendering its visible children as a list. Testing for activeChild as well as active in the secondary menu is important, as omitting it means that visitors to grandchild pages see no item highlighted at all. The approach differs from what was possible with Textpattern, where a single composite menu could drill down through the full hierarchy, but displaying one menu for pages within a given section alongside another showing the other sections proves to be a workable and context-sensitive alternative.
Setting Up RSS Feeds
Because Grav does not support the generation of RSS feeds out of the box, it needs a plugin and some extra configuration. The latter means that you need to get your head around the Grav concept of a collection because without it, you will not see anything in your feed. In contrast, database-driven platforms like WordPress or Drupal push out the content by default, which may mean that you are surprised when you first come across how Grav needs you to specify the collections explicitly.
There are two details that make performing configuration of a feed straightforward once understood. The first is that Grav routes do not match physical folder paths: a folder named 03.deliberations on disk is referenced in configuration as /deliberations, since the numeric prefix controls navigation ordering but does not appear in the route, that is the actual web page address. The second is the choice between @page.children, which collects only the immediate children of a folder, and @page.descendants, which collects recursively through all subdirectories. The collection definition belongs in the feed page's front matter, specifying where the content lives, how it should be ordered and in which direction.
Where All This Led
Once I got everything set in place, the end results were pleasing, with much learned along the way. Web page responsiveness was excellent, an experience enhanced by the caching of files. In the above discussion, I hardly mentioned the transition of existing content. For one subsite, this was manual because the scale was smaller, and the Admin plugin's interface made everything straightforward such that all was in place after a few hours of work. In the case of the other, the task was bigger, so I fell on an option used for a WordPress to Hugo migration: Python scripting. That greatly reduced the required effort, allowing me to focus on other things like setting up a modular landing page. The whole migration took around two weeks, all during time outside my client work. There are other places where I can use Grav, which surely will add to what I already have learned. My dalliance with Textpattern is feeling a little like history now.
Preventing authentication credentials from entering Git repositories
Keeping credentials out of version control is a fundamental security practice that prevents numerous problems before they occur. Once secrets enter Git history, remediation becomes complex and cannot guarantee complete removal. Understanding how to structure projects, configure tooling, and establish workflows that prevent credential commits is essential for maintaining secure development practices.
Understanding What Needs Protection
Credentials come in many forms across different technology stacks. Database passwords, API keys, authentication tokens, encryption keys, and service account credentials all represent sensitive data that should never be committed. Configuration files containing these secrets vary by platform but share common characteristics: they hold information that would allow unauthorised access to systems, data, or services.
# This should never be in Git
database:
password: secretpassword123
api:
key: sk_live_abc123def456
# Neither should this
DB_PASSWORD=secretpassword123
API_KEY=sk_live_abc123def456
JWT_SECRET=mysecretkey
Even hashed passwords require careful consideration. Whilst bcrypt hashes with appropriate cost factors (10 or higher, requiring approximately 65 milliseconds per computation) provide protection against immediate exploitation, they still represent sensitive data. The hash format includes a version identifier, cost factor, salt, and hash components that could be targeted by offline attacks if exposed. Plaintext credentials offer no protection whatsoever and represent immediate critical exposure.
Establishing Git Ignore Rules from the Start
The foundation of credential protection is proper .gitignore configuration established before any sensitive files are created. This proactive approach prevents problems rather than requiring remediation after discovery. Begin every project by identifying which files will contain secrets and excluding them immediately.
# Credentials and secrets
.env
.env.*
!.env.example
config/secrets.yml
config/database.yml
config/credentials/
# Application-specific sensitive files
wp-config.php
settings.php
configuration.php
appsettings.Production.json
application-production.properties
# User data and session storage
/storage/credentials/
/var/sessions/
/data/users/
# Keys and certificates
*.key
*.pem
*.p12
*.pfx
!public.key
# Cache and logs that might leak data
/cache/
/logs/
/tmp/
*.log
The negation pattern !.env.example demonstrates an important technique: excluding all .env files whilst explicitly including example files that show structure without containing secrets. This pattern ensures that developers understand what configuration is needed without exposing actual credentials.
Notice the broad exclusions for entire categories rather than specific files. Excluding *.key prevents any private key files from being committed, whilst !public.key allows the explicit inclusion of public keys that are safe to share. This defence-in-depth approach catches variations and edge cases that specific file exclusions might miss.
Separating Examples from Actual Configuration
Version control should contain example configurations that demonstrate structure without exposing secrets. Create .example or .sample files that show developers what configuration is required, whilst keeping actual credentials out of Git entirely.
# config/secrets.example.yml
database:
host: localhost
username: app_user
password: REPLACE_WITH_DATABASE_PASSWORD
api:
endpoint: https://api.example.com
key: REPLACE_WITH_API_KEY
secret: REPLACE_WITH_API_SECRET
encryption:
key: REPLACE_WITH_32_BYTE_ENCRYPTION_KEY
Documentation should explain where developers obtain the actual values. For local development, this might involve running setup scripts that generate credentials. For production, it involves deployment processes that inject secrets from secure storage. The example file serves as a template and checklist, ensuring nothing is forgotten whilst preventing accidental commits of real secrets.
Using Environment Variables
Environment variables provide a standard mechanism for separating configuration from code. Applications read credentials from the environment rather than from files tracked in Git. This pattern works across virtually all platforms and languages.
// Instead of hardcoding
$db_password = 'secretpassword123';
// Read from environment
$db_password = getenv('DB_PASSWORD');
// Instead of requiring a config file with secrets
const apiKey = 'sk_live_abc123def456';
// Read from environment
const apiKey = process.env.API_KEY;
Environment files (.env) provide convenience for local development, but must be excluded from Git. The pattern of .env for actual secrets and .env.example for structure becomes standard across many frameworks. Developers copy the example to create their local configuration, filling in actual values that never leave their machine.
Implementing Pre-Commit Hooks
Pre-commit hooks provide automated checking before changes enter the repository. These hooks scan staged files for patterns that match secrets and block commits when suspicious content is detected. This automated enforcement prevents mistakes that manual review might miss.
The pre-commit framework manages hooks across multiple repositories and languages. Installation is straightforward, and configuration defines which checks run before each commit.
pip install pre-commit
Create a configuration file defining which hooks to run:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-added-large-files
- id: check-json
- id: check-yaml
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']
Install the hooks in your repository:
pre-commit install
Now every commit triggers these checks automatically. The detect-private-key hook catches SSH keys and other private key formats. The detect-secrets hook uses entropy analysis and pattern matching to identify potential credentials. When suspicious content is detected, the commit is blocked and the developer is alerted to review the flagged content.
Configuring Git-Secrets
The git-secrets tool from AWS specifically targets secret detection. It scans commits, commit messages, and merges to prevent credentials from entering repositories. Installation and configuration establish patterns that identify secrets.
# Install via Homebrew
brew install git-secrets
# Install hooks in a repository
cd /path/to/repo
git secrets --install
# Register AWS patterns
git secrets --register-aws
# Add custom patterns
git secrets --add 'password\s*=\s*["\047][^\s]+'
git secrets --add 'api[_-]?key\s*=\s*["\047][^\s]+'
The tool maintains a list of prohibited patterns and scans all content before allowing commits. Custom patterns can be added to match organisation-specific secret formats. The --register-aws command adds patterns for AWS access keys and secret keys, whilst custom patterns catch application-specific credential formats.
For teams, establishing git-secrets across all repositories ensures consistent protection. Template directories provide a mechanism for automatic installation in new repositories:
# Create a template with git-secrets installed
git secrets --install ~/.git-templates/git-secrets
# Use the template for all new repositories
git config --global init.templateDir ~/.git-templates/git-secrets
Now, every git init automatically includes secret scanning hooks.
Enabling GitHub Secret Scanning
GitHub Secret Scanning provides server-side protection that cannot be bypassed by local configuration. GitHub automatically scans repositories for known secret patterns and alerts repository administrators when matches are detected. This works for both new commits and historical content.
For public repositories, secret scanning is enabled by default. For private repositories, it requires GitHub Advanced Security. Enable it through repository settings under Security & Analysis. GitHub maintains partnerships with service providers to detect their specific secret formats, and when a partner pattern is found, both you and the service provider are notified.
The scanning covers not just code but also issues, pull requests, discussions, and wiki content. This comprehensive approach catches secrets that might be accidentally pasted into comments or documentation. The detection happens continuously, so even old content gets scanned when new patterns are added.
Custom patterns extend detection to organisation-specific secret formats. Define regular expressions that match your internal API key formats, authentication tokens, or other proprietary credentials. These custom patterns apply across all repositories in your organisation, providing consistent protection.
Structuring Projects for Credential Isolation
Project structure itself can prevent credentials from accidentally entering Git. Establish clear separation between code that belongs in version control and configuration that remains environment-specific. Create dedicated directories for credentials and ensure they are excluded from tracking.
project/
├── src/ # Code - belongs in Git
├── tests/ # Tests - belongs in Git
├── config/
│ ├── app.yml # General config - belongs in Git
│ ├── secrets.example.yml # Example - belongs in Git
│ └── secrets.yml # Actual secrets - excluded from Git
├── credentials/ # Entire directory excluded
│ ├── database.yml
│ └── api-keys.json
├── .env.example # Example - belongs in Git
├── .env # Actual secrets - excluded from Git
└── .gitignore # Defines exclusions
This structure makes it obvious which files contain secrets. The credentials/ directory is clearly separated from source code, and its exclusion from Git is explicit. Developers can see at a glance that this directory requires different handling.
Documentation should explain the structure and the reasoning behind it. New team members need to understand why certain directories are empty in their fresh clones and where to obtain the configuration files that populate them. Clear documentation prevents confusion and ensures everyone follows the same patterns.
Managing Development Credentials
Development environments require credentials but should never use production secrets. Generate separate development credentials that provide access to development resources only. These credentials can be less stringently protected whilst still not being committed to Git.
Development credential management varies by organisation size and infrastructure. For small teams, shared development credentials stored in a team password manager might suffice. For larger organisations, each developer receives individual credentials for development resources, with access controlled through identity management systems.
Some teams commit development credentials intentionally, arguing that development databases contain no sensitive data and convenience outweighs risk. This approach is controversial and depends on your security model. If development credentials can access any production resources or if development data has any sensitivity, they must be protected. Even purely synthetic development data might reveal business logic or system architecture worth protecting.
The safer approach maintains the same credential handling patterns across all environments. This ensures that developers build habits that prevent production credential exposure. When development and production follow identical patterns, muscle memory built during development prevents mistakes in production.
Provisioning Production Credentials
Production credentials should never touch developer machines or version control. Deployment processes inject credentials at runtime through environment variables, secret management services, or deployment-time configuration.
Continuous deployment pipelines read credentials from secret stores and make them available to applications without exposing them to humans. GitHub Actions, GitLab CI, Jenkins, and other CI/CD systems provide secure variable storage that is injected during builds and deployments.
# .github/workflows/deploy.yml
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy to production
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
API_KEY: ${{ secrets.API_KEY }}
run: |
./deploy.sh
The secrets.DB_PASSWORD syntax references encrypted secrets stored in GitHub's secure storage. These values are never exposed in logs or visible to anyone except during the deployment process. The deployment script receives them as environment variables and can configure the application appropriately.
Secret management services like HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, or Google Cloud Secret Manager provide centralised credential storage with access controls, audit logging, and automatic rotation. Applications authenticate to these services and retrieve credentials at runtime, ensuring that secrets are never stored on disk or in environment files.
Rotating Credentials Regularly
Regular credential rotation limits exposure duration if secrets are compromised. Establish rotation schedules based on credential sensitivity and access patterns. Database passwords might rotate quarterly, API keys monthly, and authentication tokens weekly or daily. Automated rotation reduces operational burden and ensures consistency.
Rotation requires coordination between secret generation, distribution, and application updates. Secret management services can automate much of this process, generating new credentials, updating secure storage, and triggering application reloads. Manual rotation involves generating new credentials, updating all systems that use them, and verifying functionality before disabling old credentials.
The rotation schedule balances security against operational complexity. More frequent rotation provides better security but increases the risk of service disruption if processes fail. Less frequent rotation simplifies operations but extends exposure windows. Find the balance that matches your risk tolerance and operational capabilities.
Training and Culture
Technical controls provide necessary guardrails, but security ultimately depends on people understanding why credentials matter and how to protect them. Training should cover the business impact of credential exposure, the techniques for keeping secrets out of Git, and the procedures for responding if mistakes occur.
New developer onboarding should include credential management as a core topic. Before developers commit their first code, they should understand what constitutes a secret, why it must stay out of Git, and how to configure their local environment properly. This prevents problems rather than correcting them after they occur.
Regular security reminders reinforce good practices. When new secret types are introduced or new tools are adopted, communicate the changes and update documentation. Security reviews should check credential handling practices, not just looking for exposed secrets, but also verifying that proper patterns are followed.
Creating a culture where admitting mistakes is safe encourages early reporting. If a developer accidentally commits a credential, they should feel comfortable immediately alerting the security team, rather than hoping no one notices. Early detection enables faster response and reduces damage.
Responding to Detection
Despite best efforts, secrets sometimes enter repositories. Rapid response limits damage. Immediate credential rotation assumes compromise and prevents exploitation. Removing the file from future commits whilst leaving it in history provides no security benefit, as the exposure has already occurred.
Tools like BFG Repo-Cleaner can remove secrets from Git history, but this is complex and cannot guarantee complete removal. Anyone who cloned the repository before clean-up retains the compromised credentials in their local copy. Forks, clones on other systems, and backup copies may all contain the secrets.
The most reliable response is assuming the credential is compromised and rotating it immediately. History clean-up can follow as a secondary measure to reduce ongoing exposure, but it should never be the primary response. Treat any secret that entered Git as if it were publicly posted because once in Git history, it effectively was.
Continuous Improvement
Credential management practices should evolve with your infrastructure and team. Regular reviews identify gaps and opportunities for improvement. When new credential types are introduced, update .gitignore patterns, secret scanning rules, and documentation. When new developers join, gather feedback on clarity and completeness of onboarding materials.
Metrics help track effectiveness. Monitor secret scanning alerts, track rotation compliance, and measure time-to-rotation when credentials are exposed. These metrics identify areas needing improvement and demonstrate progress over time.
Summary
Preventing credentials from entering Git repositories requires multiple complementary approaches. Establish comprehensive .gitignore configurations before creating any credential files. Separate example configurations from actual secrets, keeping only examples in version control. Use environment variables to inject credentials at runtime rather than storing them in configuration files. Implement pre-commit hooks and server-side scanning to catch mistakes before they enter history. Structure projects to clearly separate code from credentials, making it obvious what belongs in Git and what does not.
Train developers on credential management and create a culture where security is everyone's responsibility. Provision production credentials through deployment processes and secret management services, ensuring they never touch developer machines or version control. Rotate credentials regularly to limit exposure windows. Respond rapidly when secrets are detected, assuming compromise and rotating immediately.
Security is not a one-time configuration but an ongoing practice. Regular reviews, continuous improvement, and adaptation to new threats and technologies keep credential management effective. The investment in prevention is far less than the cost of responding to exposed credentials, making it essential to get right from the beginning.
Related Resources
WordPress Starter Themes: From bare foundations to modern workflows
Starter themes have long occupied an important place in WordPress development. They sit between a completely blank project and a fully styled off-the-shelf theme, offering enough structure to speed up work without dictating how the finished site must look. For agencies, freelancers and in-house teams, that balance can save considerable time, allowing developers to begin with a lean codebase and concentrate on the parts that make each site distinct.
That broad appeal helps explain why starter themes continue to evolve in different directions. Some remain deliberately minimal and close to WordPress core conventions, while others embrace modern tooling such as Composer, Vite, Tailwind CSS and component-based templating. Alongside these are starter themes intended for visual builders and users who want a gentler route into customisation. Taken together, they demonstrate that there is no single definition of a WordPress starter theme, with the common thread being that each provides a starting point rather than a finished product.
wd_s: A Generator-Driven Approach
wd_s is a generator-driven starter theme from WebDevStudios. The wd_s generator makes the setup process more tailored by asking for project details: name, URL, description, namespace, text domain, author, author URL, author email and development URL. Once those details are entered, the script performs a find-and-replace and delivers a ZIP file ready to extract into wp-content/themes. The process is straightforward, though it also reflects a more structured approach to project setup than many starter themes provide.
The generator highlights details that matter in real-world theme development, but are sometimes overlooked when beginning from a generic scaffold. Name-spacing, text domains and project metadata all play a part in maintainability and localisation, and wd_s brings those choices to the fore from the very beginning. The example values shown by the generator, such as "Acme Inc." for the name field and a namespace using underscores, are illustrative rather than prescriptive. What stands out more than any one field is the intention to reduce repetitive manual setup and encourage consistency from the very start of a project.
Sage: A Modern Development Workflow
Roots Sage represents a significant shift towards a modern development workflow. Sage is a Tailwind CSS WordPress starter theme with Laravel Blade templating, currently at version 11.1.0 and with over 13,191 GitHub stars at the time of writing. Setup uses Composer and NPM rather than a simple ZIP download, and a typical installation begins in wp-content/themes with composer create-project roots/sage my-theme, followed by npm install and npm run build.
That workflow signals the audience Sage is aimed at. Rather than merely wrapping WordPress templates in a minimal theme shell, Sage introduces tooling and conventions familiar to developers who work with Laravel and modern front-end stacks. The Vite build process generates files including a manifest, compiled CSS and JavaScript and a theme.json, completing the whole build in a matter of seconds and demonstrating that WordPress development can be integrated into contemporary asset pipelines without giving up compatibility with the CMS.
Blade Templating and Component-Driven Design
Blade templating is central to Sage's proposition. The base layout in resources/views/layouts/app.blade.php shows a clean separation of structure and content using directives such as @include, @yield and @hasSection. Header and footer hooks still call familiar WordPress functions including wp_head, wp_body_open and wp_footer, but the surrounding syntax is closer to Laravel than traditional PHP-heavy WordPress templates. This gives developers access to template inheritance, reusable components and directives, making larger codebases considerably easier to organise.
Reusable components illustrate this style compactly. Properties define type and message, a PHP match expression selects classes based on alert type and the component merges those classes into its final markup. The result is not merely an isolated snippet but a demonstration of how Sage encourages component-driven design, reducing repetition and making presentation logic easier to follow, particularly in projects with many shared interface elements.
Tailwind CSS and the Block Editor
Sage places strong emphasis on integrating Tailwind CSS with the WordPress block editor. It automatically generates theme.json from the Tailwind configuration, making colours, font families and sizes immediately available in the block editor with zero additional configuration. The sample app.css imports Tailwind and points at views and app files as content sources, while the generated theme.json includes settings for layout, background, colour palettes, spacing and typography. The palette includes eleven shades of grey along with black and white, and the typography settings mirror Tailwind's familiar scale from xs through 9xl.
This addresses a long-standing friction point on WordPress theming: keeping front-end design systems in sync with the editing experience. In older workflows, editor styles and front-end styles often drifted apart, creating extra maintenance work and inconsistency for content editors. Sage's approach narrows that gap by deriving editor settings directly from the same Tailwind configuration used for the front end, with theme.json generated during the build process rather than maintained by hand.
Theme Structure and the Roots Ecosystem
The theme structure for Sage reinforces its emphasis on organisation. The app directory contains providers, view composers, filters.php and setup.php, while resources holds CSS, JavaScript and Blade views grouped into components, layouts, partials and sections. Public assets, composer.json, package.json, theme.json and vite.config.js complete the structure, paired with PSR-4 autoloading, service providers and Acorn, which brings Laravel-style patterns into WordPress. The Vite configuration includes Tailwind, the Laravel Vite plugin and Roots plugins for WordPress support and theme.json generation, plus aliases for scripts, styles, fonts and images.
Another notable feature is hot module replacement in the WordPress block editor, with style changes updating instantly without page refreshes. Sage sits within the broader Roots ecosystem, which also includes Bedrock (a WordPress boilerplate for Composer and Git-based projects), Trellis (a server provisioning and deployment tool), Acorn, Radicle (which bundles the entire Roots stack into a single starting point) and WP Packages (a Composer repository for WordPress plugins and themes). Testimonials on the Roots website emphasise that many developers regard this ecosystem as a route to a more structured and modern WordPress experience, with Sage having been actively maintained for over a decade.
Visual Composer Starter Theme: A Builder-Friendly Option
Not every starter theme is aimed at developers working with command-line tooling and component-based templates. The Visual Composer Starter Theme occupies a different place in the landscape, described as a free bundle of a lightweight theme and a powerful WordPress page builder. It is aimed at building blogs, WooCommerce stores, business sites and personal websites, and the language surrounding it stresses ease of use, intuitive theme options and layout customisation tools, presented as a free resource intended to support the WordPress community.
Its feature set reflects that broader audience. The theme is easy to customise through the WordPress customiser, SEO-friendly and responsive by default, covering use cases such as personal blogs, landing pages, business sites, portfolios, startups and online stores. WooCommerce compatibility receives particular emphasis, with support for adjusting design preferences via the customiser and building online shops at no cost. Hero and featured images, unlimited colour options, page-level design controls and a choice between regular and mobile sandwich-style menus are all included.
There is also a strong focus on compatibility. The theme is fully translation-ready and compatible with WPML, qTranslate and Polylang, while support for Advanced Custom Fields and Toolset for custom post type development is highlighted. It is also presented as ready to combine with the Visual Composer website builder and is developed openly on GitHub, where anyone can contribute. This is less about offering an unvarnished code scaffold and more about giving users a flexible visual base with a broad range of built-in options, though it remains part of the starter theme conversation because it is designed to be extended rather than merely installed and left untouched.
Bones: Speed, Control and Pragmatism
Bones, designed and developed by Eddie Machado, returns more closely to the classic developer-oriented concept while retaining a distinctive voice. It is described as an HTML5, mobile-first starter theme for rapid WordPress development, and it makes clear that it is not a framework. Frameworks can introduce their own conventions and complexity, whereas Bones is designed to be as bare and minimalistic as possible, intended to be used on a per-project basis with no child themes.
The mobile-first emphasis is one of Bones' defining characteristics. Its Sass setup serves minimal resources to smaller screens before scaling up for larger viewports, an approach tied to performance as well as responsiveness, and Bones includes extensive comments and examples to help developers get started with Sass. It also provides a well-documented example for custom post types and functions to customise the WordPress admin area for clients, though these are entirely optional and can be removed if not needed. The project is released under the WTFPL, one of the most permissive licences available, and takes pride in removing unnecessary elements from the WordPress header to keep output clean and lightweight. The philosophy is to keep what is useful and discard the rest, building from a solid and speedy foundation.
Selecting a Starter Theme to Match Your Workflow
When viewed together, these themes reveal how varied the starter theme category has become. A Speckyboy roundup of top starter and bare-bones themes for WordPress development in 2026 (last updated on the 8th of March 2026) places Sage alongside newer and more editor-focused options including Blockbase, GeneratePress, Air, WDS BT, Byvex and Flynt. The roundup notes that every website serves different goals and that WordPress is flexible enough to support them all, but also makes clear that starting each project from scratch leads to repeated work, with starter themes offering a way to avoid that repetition while preserving freedom over design and functionality.
The same roundup provides a useful framework for evaluating starter themes. Ongoing maintenance matters because themes need to keep pace with WordPress and surrounding technologies, and themes that have not been updated in years should be avoided. The distinction between classic and block themes is important, since developers need a starting point that aligns with their preferred editing model. Features that genuinely speed development, whether block patterns, a comprehensive settings panel or development tools, can make a significant difference over time. A starter theme should also stay out of the way rather than burden projects with an opinionated design direction, and compatibility with a preferred editor or page builder remains central to choosing well.
Whether the preference is for the generator-based setup of wd_s, the modern tooling of Sage, the builder-friendly versatility of Visual Composer Starter Theme or the stripped-back classic structure of Bones, each represents a different answer to the same challenge. Developers and site builders often need a head start rather than a finished design, and a good starter theme provides exactly that, while leaving enough room for the final result to become something entirely its own.
Generating Git commit messages automatically using aicommit and OpenAI
One of the overheads of using version control systems like Subversion or Git is the need to create descriptive messages for each revision. Now that GitHub has its copilot, it now generates those messages for you. However, that still leaves anyone with a local git repository out in the cold, even if you are uploading to GitHub as your remote repo.
One thing that a Homebrew update does is to highlight other packages that are available, which is how I got to learn of a tool that helps with this, aicommit. Installing is just a simple command away:
brew install aicommit
Once that is complete, you now have a tool that generates messages describing very commit using GPT. For it to work, you do need to get yourself set up with OpenAI's API services and generate a token that you can use. That needs an environment variable to be set to make it available. On Linux (and Mac), this works:
export OPENAI_API_KEY=<Enter the API token here, without the brackets>
Because I use this API for Python scripting, that part was already in place. Thus, I could proceed to the next stage: inserting it into my workflow. For the sake of added automation, this uses shell scripting on my machines. The basis sequence is this:
git add .
git commit -m "<a default message>"
git push
The first line above stages everything while the second commits the files with an associated message (git makes this mandatory, much like Subversion) and the third pushes the files into the GitHub repository. Fitting in aicommit then changes the above to this:
git add .
aicommit
git push
There is now no need to define a message because aicommit does that for you, saving some effort. However, token limitations on the OpenAI side mean that the aicommit command can fail, causing the update operation to abort. Thus, it is safer to catch that situation using the following code:
git add .
if ! aicommit 2>/dev/null; then
echo " aicommit failed, using fallback"
git commit -m "<a default message>"
fi
git push
This now informs me what has happened when the AI option is overloaded and the scripts fallback to a default option that is always available with git. While there is more to my git scripting than this, the snippets included here should get across how things can work. They go well for small push operations, which is what happens most of the time; usually, I do not attempt more than that.
Loading API Keys from Linux shell environment variables in Python with Dotenv
Recently, I ran into trouble with getting Python to pick up an API key that I had defined in the underlying bash environment. This was within a Python console running inside the Positron IDE for R and Python scripting. Opening up the folder containing my Python scripts within the IDE was part of the solution. The next part was creating a .env file within the same folder. A line like this was added within the new file:
export API_KEY="<API key value>"
That meant that code like the following then read in the API key in a more robust manner:
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv('API_KEY', 'default_value')
This imports the os module and the load_dotenv method from the dotenv package. Then, load_dotenv is executed to load the .env file and its contents. After that, the os.getenv function can assign the API key to a Python variable from the value of the environment variable.
Since this also was within a Git repository, a .gitignore file needed creating with the contents .env to avoid that file being uploaded to GitHub, which is the last place where you should be storing credentials like passwords, passphrases and API keys. While my repository may be private, the state of things at these troubled times mean that even that is no failsafe.
An alternate approach to setting up a local Git repository with a remote GitHub connection
For some reason, I ended with two versions of this at the draft stage, forcing me to compare and contrast before merging them together to produce what you find here. The inspiration was something that I encountered a while ago: getting a local repository set up in a perhaps unconventional manner.
The simpler way of working would be to set up a repo on GitHub and clone it to the local machine, yet other needs are the cause of doing things differently. In contrast, this scheme stars with initialising the local directory first using the following command after creating it with some content and navigating there:
git init
This marks the directory as a Git repository, allowing you to track changes by creating a hidden .git directory. Because security measures often require verification of directories when executing Git commands, it is best to configure a safe directory with the following command to avoid any issues:
git config --global --add safe.directory [path to directory]
In the above, replace the path with your specific project directory. This ensures that Git recognises your directory as safe and authorised for operations, avoiding any messages whenever you work in there.
With that completed, it is time to add files to the staging area, which serves as an area where you can review and choose changes to be committed to the repository. Here are two commands that show different ways of accomplishing this:
git add README.md
git add .
The first command stages the README.md file, preparing it for the next commit, while the second stages all files in the directory (the . is a wildcard operator that includes everything in there).
Once your files are staged, you are ready to commit them. A commit is essentially a snapshot of your changes, marking a specific point in the project's history. The following command will commit your staged changes:
git commit -m "first commit"
The -m flag allows you to add a descriptive message for context; here, it is "first commit." This message helps with understanding the purpose of the commit when reviewing project history.
Before pushing your local files online, you will need to create an empty repository on GitHub using the GitHub website if you do not have one already. While still on the GitHub website, click on the Code button and copy the URL shown under the HTTPS tab that is displayed. This takes the form https://github.com/username/repository.gitand is required for running the next command in your local terminal session:
git remote add origin https://github.com/username/repository.git
This command establishes a remote connection under the alias origin. By default, Git sets the branch name to 'master'. However, recent conventions prefer using 'main'. To rename your branch, execute:
git branch -M main
This command will rename your current branch to 'main', aligning it with modern version control standards. Finally, you must push your changes from the local repository to the remote repository on GitHub, using the following command:
git push -u origin main
The -u flag sets the upstream reference, meaning future push and pull operations will default to this remote branch. This last step completes the process of setting up a local repository, linking it to a remote one on GitHub, staging any changes, committing these and pushing them to the remote repository.
Avoiding repeated token requests by installing the Git credential helper on Linux Mint
On a new machine, I found asking for the same access token repeatedly. Since this is a long string, that is convenient and does not take long to become irritating. Thus, I sought a way to make it more streamlined. My initial attempt produced the following message:
git: 'credential-libsecret' is not a git command
The main cause for the above was the absence of the libsecret credential helper, crucial for managing credentials securely in a keyring, from my system. The solution was to install the required packages from the command line:
sudo apt install libsecret-1-0 libsecret-1-dev
Following installation, the next step was to navigate to the appropriate directory and execute the make command to compile the files within the directory, transforming them into an executable credential helper:
cd /usr/share/doc/git/contrib/credential/libsecret; sudo make
With the credential helper fully built, Git needed to be configured to use it by executing the following:
git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret
Since one error message is enough for any new activity, it made sense to confirm that the credential helper resided in the correct location. That was accomplished by issuing this command:
ls -l /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret
All was well in my case, saving the need to reinstall Git or repeat the manual compilation of the credential helper. When all was done, I was ready to automate things further.
Version control of large files on GitHub
When you try pushing large files to a GitHub repository, you may find that you breach its 100 MB limit. When you do, you either need to buy a data pack or exclude the file from being tracked. In my case, I decided that the monthly fee for 50 GB was not overly onerous, so I added that. Excluding such files using the .gitignore functionality makes a lot of sense, too.
If you decide to proceed as I did, you will need to install git-lfs. Since that may vary by operating system, I am leaving to you to look for those details on the website that I have linked to earlier. Activating it for your user account needs the following:
git lfs install
Following that, you need to flag the file or type of file using a command like the following:
git lfs track "[file path with name or search pattern]"
Executing the above adds the file path including the file name or the search pattern (normal operating system wildcards like * work here) to a file named .gitattributes in the root of the repository folder hierarchy. If that file no longer exists, it will get created the first time that this is done. It will also need to be added to the repository using git add like any other file. A general command like the following will also do it anyway, since it covers everything in the relevant folder:
git add .
After making a commit, the next step is to push the contents into GitHub. At this stage, the large file or files will be recognised and sent to large file storage with only a text link in the main area. Everything else will be handled as normal.
While on this subject, I need to add a few words of warning. Pushing a large file to GitHub without doing things up front will cause the operation to fail. That may make the transition over to large file storage all the more tricky, since things will be out of order. Moving everything to a temporary folder and again cloning the repository was how I got out of this impasse when it happened to me. Then, I could get the large file handling set up before getting going again. It is better to sort things like this out at the start of the process, rather than attempting to remedy things part way through the process.
Keeping a file or directory out of a Git or GitHub repository
Recently, I have begun to do more version control of files with Git and GitHub. However, GitHub is not a place to keep files with log in credentials. Thus, I wanted to keep these locally but avoid having them being tracked in either Git or GitHub.
Adding the names to a .gitignore file will avoid their inclusion prospectively, but what can you do if they get added in error before you do? The answer that I found is to execute a command like the following:
git rm -r --cached [path to file or directory with its name]
That takes it out of the staging area and allows the .gitignore functionality to do its job. The -r switch makes the command recursive, should you be working with the contents of a directory. Then, the --cached flag is what does the removal from the staging area.
While the aforementioned worked for me when I had an oversight, the following is also suggested:
git update-index --assume-unchanged [path to file or directory with its name]
That may be working without a .gitignore file, which was not how I was doing things. Nevertheless, it may have its uses for someone else, so that is why I include it above.