Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
156672 stories
·
33 followers

JetBrains Air lands on Windows

1 Share

We’re excited to welcome Windows developers to JetBrains Air!

Download for x64 / Download for ARM64

Since we launched the JetBrains Air, Windows support has been one of the most requested updates from the developer community. We heard that feedback and worked hard to bring Air to Windows.

Air is built for agent-agnostic development – you can use leading AI coding agents to implement features, fix bugs, investigate code, generate changes, and review results in a dedicated agent-first development environment. With the Windows release, more developers can now bring Air into their daily workflow.

Development workflows you can try in Air

The Air desktop app fills the gap between running agents in the terminal and working in a full IDE. It gives you a dedicated environment for agentic development, where you can run tasks, review results, and keep your main coding workflow focused.

Here are a few simple workflows to get started with Air.

Start with a plan and precise task context

Start in Plan mode and describe what you want to implement. Air saves your execution plan to a markdown file before implementation begins, so you can iterate on it, leave comments, reference classes, folders, files, docs, symbols, or exact lines, and upload files from your computer as additional context.

Then choose which agent executes the task and where it runs: locally, or in a Git worktree.

Let the agent make changes, then review the diff before deciding what to keep. Better context leads to better output.

Implement several features with agents running in parallel

Run agents simultaneously in separate Git worktrees. Each agent gets its own working directory and branch, so their changes stay independent and don’t conflict.

Review the results, apply the changes you want, and commit them to main when ready.

Review code from one agent using a different agent

Use one agent to implement a task and another to review the result. For example, you can ask Claude to handle the implementation and Codex to review the changes.

The reviewing agent leaves comments directly in code. You can choose what is worth improving, add your own comments, and send the task back to the original agent. In Air, you can build this kind of multi-agent workflow without additional workarounds.


Over the past few months, we invested heavily in app stability. The goal was simple: make sure Windows developers can start using Air without workarounds. We’ve learned a lot since the first release, and we wanted Windows developers to have a reliable experience from day one.

Try JetBrains Air for Windows and let us know what you want us to improve next.

Try for x64 / Try for ARM64

Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

The Shifting Line Between CSS States and JavaScript Events

1 Share

CSS is listening to us. No, not like that. Rather, CSS is accumulating more and more pseudo-classes to help us respond to JavaScript events so that we don’t have to do so with JavaScript itself. But while pseudo-classes track states, not events, they sure can feel like event listeners sometimes (not that it really matters in the context of CSS).

Then again, what is CSS these days? For example, there’s a proposal for event-trigger in the Animation Triggers spec, which would basically listen for events and trigger animations. If you ask me though, the syntax is capable of a lot more than that (think: invoker commands but for CSS).

But to stay in today’s reality, I’ll walk you through the different CSS pseudo-classes out there that are kind of like event listeners, before doing the same for event-trigger, where I’ll show you how (I think) this currently unsupported feature would work.

“Event listening” pseudo-classes

:hover and :active

The :hover state captures the moment from when the pointerenter event fires to when the pointerleave event fires, which perfectly illustrates why pseudo-classes are states, not events.

:active matches the target (e.g., a link or button) that’s currently being pressed with a mouse, finger, or stylus, which makes it akin to pointerdown and pointerup/pointercancel.

By the way, the pointer-events: none CSS declaration prevents pointer events from firing on the selected element!

:focus and :focus-visible

The :focus pseudo-class is akin to the focus and blur (unfocus) JavaScript events, but :focus-visible is a bit more complex. :focus-visible triggers when :focus does, but in addition, the browser uses a variety of heuristics to determine whether or not a focus indicator should be shown. Is the user operating with a keyboard? Is the element a form control? This really makes me appreciate what CSS offers. In fact, the best way to handle this using JavaScript is to query the CSS pseudo-class:

element.addEventListener("focus", (event) => {
  if (event.target.matches(":focus-visible")) {
    /* Do something */
  }
});

:focus-within (and :has())

JavaScript excels at the “if A is Y, then do Z to B” kind of stuff. We can traverse the DOM, leverage event propagation, and much more. In that regard, CSS can feel a bit limited. However, CSS is evolving quickly. It has many new if-this-do-that features such as scroll-driven animations, and it’ll have more in the future. HTML is doing the same with dedicated components such as <details>, which all have accompanying CSS features.

I’ll mention some of that later, actually. In a more holistic sense, what we have is :focus-within, which matches if a child has focus, and :has(), which accepts any valid selector and matches if such a relationship exists between the two selectors.

For example, these two selectors do the exact same thing:

form:focus-within {
  /* Style the form when something within has focus */
}

form:has(:focus) {
  /* Style the form when something within has focus */
}

:checked

It’s fairly obvious what :checked does. The JavaScript event that’s most synonymous with it is change, which fires when the value of an <input>, <select>, or <textarea> changes (although, in this context, the input event is quite similar).

To listen for a check, we’d do something like this:

checkbox.addEventListener("change", (event) => {
  if (event.target.checked) {
    /* Checked */
  } else {
    /* Not checked */
  }
});

CSS pseudo-classes often capture the moment between two JavaScript events (e.g., pointerenter and pointerleave), but when they’re not doing that, they’re handling logic instead, as above.

Let’s look at some more examples of hidden logic handling.

:valid/:invalid/:user-valid/:user-invalid/:autofill

We don’t need the :not() pseudo-class function here, as validity can be checked using both the :valid and :invalid pseudo-classes, but on the JavaScript side of things, there’s no valid event (only invalid). That being said, if using JavaScript, you’ll likely want to call the checkValidity() method (which actually fires the invalid event if it returns false) within the callback of the event listener for input, change, blur (to check validity when unfocusing from an element), or submit (to check validity of the entire form when submitting it, as below).

form.addEventListener("submit", () => {
  if (form.checkValidity()) {
    /* All form controls are valid */
  } else {
    /* A form control is invalid (the invalid event fires) */
  }
});

We can also do this with the ValidityState object, which doesn’t fire the invalid event, but does tell us why a form control is valid or invalid in the same way that HTML form validation does:

input.addEventListener("input", () => {
  if (input.validity.valid) {
    /* Input is valid */
  } else {
    /* Input is invalid (the invalid event doesn’t fire) */
  }
});

The thing about HTML form validation is that it takes care of the entire front end, but if there’s a non-default behavior that you need, checkValidity() or ValidityState is what you’re looking for.

The pseudo-classes will work either way. A little too well, in fact! An easy thing to miss is that form controls trigger either :valid or :invalid immediately. However, :user-valid and :user-invalid wait for users to supply a value and unfocus before triggering. This is actually what the change event does (unless the element is a checkbox, radio button, dropdown list, color picker, or range slider), and what makes it different from the input event.

There isn’t a JavaScript event for auto-filling or even a clean way to detect it using JavaScript, but there is an :autofill pseudo-class.

Media element pseudo-classes

Media element pseudo-classes are still new. They aren’t supported by Chrome yet and only landed in Firefox recently, but they are a part of Interop 2026 and soon we’ll be able to style <audio> and <video> elements based on their state without listening to JavaScript events. I’m sure you understand how this works by now, so here’s a quick rundown:

Pseudo-classJavaScript event equivalent
:bufferingwaiting
:mutedvolumechange (but see below)
:pausedpause
:playingplaying (not play)
:seekingseeking
:stalledstalled
:volume-lockedN/A, see below

Use the volumechange event to detect mute:

audio.addEventListener("volumechange", () => {
  if (audio.muted) {
    // Muted
  } else {
    // Not muted
  }
});

Detecting volume lock means trying to change the volume and checking for success. The best approach is to create an entirely new element so that we don’t trigger volumechange on the real one:

// Create video
const video = document.createElement("video");

// Change volume
video.volume = 0.5;

if (video.volume !== 0.5) {
  // Volume locked
} else {
  // Volume not locked
}

(Or to use the :volume-locked pseudo-class, if writing CSS.)

:popover-open / :open / :modal

As we might expect, there’s no JavaScript event for when a popover, <dialog>, or <details> opens or closes, but we can listen for the toggle event and then check the state:

element.addEventListener("toggle", () => {
  if (element.open) {
    /* Popover/dialog/details open */
  } else {
    /* Popover/dialog/details not open */
  }
});

However, CSS offers these pseudo-classes right out of the box:

  • :popover-open (for popovers)
  • :open (for <dialog> and <details> elements)
  • :modal (for modal <dialog>s and fullscreen elements)

Speaking of fullscreen elements…

:fullscreen

The :fullscreen pseudo-class is synonymous with the fullscreenchange JavaScript event with a conditional baked in:

document.addEventListener("fullscreenchange", () => {
  if (document.fullscreenElement) {
    /* fullscreenElement is fullscreen */
  } else {
    /* Nothing is fullscreen (fullscreenElement is null) */
  }
});

:target

When a URL hash (e.g., #contact) matches an element’s ID (e.g., <div id="contact">), that element matches the :target pseudo-class. When using JavaScript, we have to listen for the hashchange event and then see if a matching element is found:

window.addEventListener("hashchange", () => {
  const target = document.getElementById(window.location.hash.substring(1));

  if (target) {
    /* Matching element found */
  } else {
    /* Matching element not found */
  }
});

Conclusion (but not really)

This isn’t a “JavaScript bad” rant but rather an appreciation for what CSS simplifies without forgetting the surgical control that JavaScript offers. More ways to do things is never a bad thing.

And on that note, I want to quickly mention event-trigger.

Actual event listeners (event-trigger)

I came across event triggers when Chrome implemented scroll-triggered animations, as they’re in the same module, but they’re not supported by any web browser yet, so if I make any mistakes, I apologize. Let’s dive in.

event-trigger-name will accept a simple dashed ident:

button {
  event-trigger-name: --event;
}

event-trigger-source will be the event listener, essentially.

It’ll accept the following keywords:

  • activate
  • interest
  • click
  • touch
  • dblclick
  • keypress(<string>)
button {
  event-trigger-source: click;
}

I believe the interest keyword refers to the upcoming Interest Invoker API whereas the activate keyword could depend on the element. For <details> for example, activation could mean when opened, but I’m not sure. Subsequent drafts of the spec should tell us more, and reveal more events.

Anyway, the events will trigger animations. First we’d create a @keyframes animation, then we’d attach it to the element to be animated, but the animation wouldn’t run until triggered by the event (whereas normally they’d run immediately).

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

div {
  animation: fade-in 300ms both;
}

Then we ensure that when the event fires, the animation triggers. We do this by setting animation-trigger alongside animation, referencing the dashed ident (--event). This has the optional benefit of allowing the event of one element to trigger the animation of another. Here’s a quick example, using the event-trigger shorthand this time:

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

button {    
  /* On click, trigger --event animation */
  event-trigger: --event click;
}

div {
  /* When --event fires, play animation forwards */
  animation-trigger: --event play-forwards;

  /* Animation */
  animation: fade-in 300ms both;
}

This is what’s called a stateless event trigger. Think about it — you can’t unclick a click, right? But we can lose interest, so here’s what a statefull event-triggered animation would look like (notice the syntax for two events separated by a / and two animation actions, one for each state):

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

button {    
  /* interest (entry) / interest (exit) */
  event-trigger: --event interest / interest;
}

div {
  /* Play forward with interest, backward when losing it */
  animation-trigger: --event play-forwards play-backwards;

  /* Animation */
  animation: fade-in 300ms both;
}

Acceptable animation actions include:

  • none
  • play
  • play-once
  • play-forwards
  • play-backwards
  • pause
  • reset
  • replay

There are many combinations of events and animation actions that wouldn’t work, but these would be easy to sidestep because it wouldn’t make sense to use them. We could, however, trigger multiple different animations because animation-trigger is a reset-only sub-property animation. Here’s a rough example:

animation-name: animationA, animationB;
animation-trigger: --eventA play, --eventB replay;

The possibilities are endless depending on how the W3C move forward with this feature (the spec mentions allowing for event bubbling!), but I kinda wish we could invoke JavaScript methods with event triggers like how HTML can with the Invoker Commands API.

What do you think? A step in the right direction, or does CSS need to stay in its lane?


The Shifting Line Between CSS States and JavaScript Events originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.

Read the whole story
alvinashcraft
11 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Getting Inherited Controller Routes to work in ASP.NET Core

1 Share
Controller inheritance in ASP.NET Core is an edge case, but if you need it you have to be mindful of how route inheritance works in ASP.NET. This post explores why concrete class inheritance causes route duplication and provides a couple of solutions.
Read the whole story
alvinashcraft
19 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Add Auth0 Documentation to Claude Code

1 Share
Eliminate context-switching during authentication development. Learn how to connect the Auth0 MCP Server to Claude Code for real-time access to the complete Auth0 documentation directly inside your terminal terminal workspace.

Read the whole story
alvinashcraft
44 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Thinking About Nothing Takes a Lot of Work

1 Share

I keep trying to think about nothing, and it turns out thinking about nothing takes a lot of work. The moment I clear the deck and try to sit in the empty space, my mind rushes in to fill it—old conversations, half-finished projects, the next thing I am supposed to be doing, the last thing I should have done differently. Nothing is apparently the one thing my brain refuses to leave alone.

I think this is why I find the quiet so hard, and also why I keep chasing it. The work I do all day is the work of filling space—words, posts, APIs, schedules, lists, plans. Producing is easy because the machinery is always running. Stopping the machinery, even for a few minutes, is the harder discipline. It takes a lot of something to arrive at nothing.

Lately I have been treating the attempt itself as the point. I am not going to win against my own mind, but I can keep showing up to the empty room and sitting in it a little longer each time. Thinking about nothing takes a lot of work—and showing up to do that work, over and over, is exactly where the practice lives.



Read the whole story
alvinashcraft
51 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

What's new in Git 2.55.0?

1 Share

The Git project recently released Git 2.55.0. Let's look at a few notable highlights from this release, which includes contributions from the Git team at GitLab.

What's covered:

git-history(1) learns fixup

In our highlights of Git 2.54.0, we covered the introduction of git-history(1). In 2.55.0, a new subcommand for this tool was added: fixup.

Imagine you've made some changes and you want to amend those changes into an existing commit. The most common approach to this is to create a fixup commit and autosquash it with git-rebase(1):

git commit --fixup=<commit-id>
git rebase -i --autosquash <commit-id>^

Doing this in two steps is clumsy, especially because it requires an interactive rebase. Instead you can use the git-history(1) fixup command:

git history fixup <commit-id>

This takes the staged changes and amends them into the given commit. As an added bonus, because you're using git-history(1), all other local branches that contain the fixed-up commit are updated as well. So when working with stacked branches, fixup-ing a commit in the stack will automatically rebase all related branches.

This feature was implemented by Patrick Steinhardt.

fsmonitor daemon for Linux

When working with large monorepos, git-status(1) can be slow to determine what changed in the local worktree because Git would need to traverse the whole working tree to see which files are modified. To speed up this process, in January 2018 a setting core.fsmonitor was added in Git 2.16. Back then, you had to provide your own tool (like Watchman). When this was configured, this tool runs in the background and monitors changes on the file system. This informs Git that a file was touched and Git then verifies whether the file was modified and updates the cached status. Then whenever the user calls git-status(1), it can simply return the cached status.

In April 2022, the setting core.fsmonitor was changed to accept a boolean value. When this setting is set to true, a daemon built-in into Git is used and no more third-party tool is needed. But this filesystem monitor was only implemented for Windows and macOS, support for GNU/Linux did not yet exist.

This changes in Git 2.55, where support for Linux has been added, too. To achieve this, inotify(7) is used. inotify(7) was chosen over fanotify(7) because fanotify(7) requires elevated privileges. This comes with a small caveat though, the fsmonitor needs to put a watcher on each and every directory in the repository. In a large repo you might hit the limit of inotify watches (fs.inotify.max_user_watches), which you may need to raise.

These changes were submitted by Paul Tarjan based on work by Eric DeCosta and Marziyeh Esipreh.

git push to a remote group

Quite some time ago git-fetch(1) learned to fetch from a group of remotes.

The following command configures a group of remotes:

git config set remotes.forks "origin upstream"

When this is set up, you can git-fetch(1) from this "forks" group, and then all the remotes in that list are fetched from. This can be useful when you want to get the updates from a set of remotes in one go.

git-push(1), however, was not able to use remote groups.

In Git 2.55, this gap is closed and git-push(1) now accepts a remote group too. For example if you want to push the main branch to the group mentioned above:

git push forks main

Similar as with git-fetch(1), this command pushes the specified refs to each of the remotes in the group. Each remote is pushed independently and honors its own remote.<name>.push mapping and mirror settings.

This feature was submitted by Usman Akinyemi, suggested by Junio C Hamano.

Limiting git log --graph lane width

The --graph option of git-log(1) draws an ASCII representation of the commit history. In a repository with many active contributors this graph can grow very wide. For example, on the git.git repository this graph grows nine lanes wide after only 30 commits:

* 26d8d94e94 A few more topics before -rc2
*   02bb39c5cb Merge branch 'js/objects-larger-than-4gb-on-windows-more'
|\
| * c6a4629e32 odb: use size_t for object_info.sizep and the size APIs
| * 7a3a78cc76 packfile,delta: drop the `cast_size_t_to_ulong()` wrappers
| * 188bac14f7 pack-objects: use size_t for in-core object sizes
| * 2d83cc3f84 packfile: widen unpack_entry()'s size out-parameter to size_t
| * 1d43315b31 pack-objects(check_pack_inflate()): use size_t instead of unsigned long
| * 33afe87338 patch-delta: use size_t for sizes
| * 8ea69373a4 compat/msvc: use _chsize_s for ftruncate
* |   8cf57cbec4 Merge branch 'kw/gitattributes-typofix'
|\ \
| * | 0bf506efd4 gitattributes: fix eol attribute for Perl scripts
* | |   8d96f09e92 Merge branch 'js/objects-larger-than-4gb-on-windows'
|\ \ \
| * | | ab3810eb6f zlib: properly clamp to uLong
* | | | 95e20213fa Hopefully final batch before -rc2
* | | |   8632b5c49d Merge branch 'en/commit-graph-timestamp-fix'
|\ \ \ \
| * | | | fbcc5408fc commit-graph: use timestamp_t for max parent generation accumulator
* | | | |   619931f561 Merge branch 'dl/posix-unused-warning-clang'
|\ \ \ \ \
| * | | | | cf48887610 compat/posix.h: simplify GIT_GNUC_PREREQ() comparison
| * | | | | ffd45926dc compat/posix.h: clean up GIT_GNUC_PREREQ() and UNUSED
| * | | | | 689dc92e50 compat/posix.h: enable UNUSED warning messages for Clang
* | | | | |   621962aa7a Merge branch 'td/ls-files-pathspec-prefilter'
|\ \ \ \ \ \
| * | | | | | 3f5203eeb4 ls-files: filter pathspec before lstat
| | |_|_|_|/
| |/| | | |
* | | | | |   0c706d5092 Merge branch 'ta/doc-config-adoc-fixes'
|\ \ \ \ \ \
| * | | | | | 4fa2c6e045 doc: git-config: escape erroneous highlight markup
| * | | | | | 042221cccb doc: config/sideband: fix description list delimiter
| * | | | | | 3eb61fda62 doc: config: terminate runaway lists
* | | | | | |   49cb068fb2 Merge branch 'jc/t1400-fifo-cleanup'
|\ \ \ \ \ \ \
| * | | | | | | e8f12e0e95 t1400: have fifo test clean after itself
* | | | | | | |   b4970f8448 Merge branch 'td/describe-tag-iteration'
|\ \ \ \ \ \ \ \
| * | | | | | | | 55088ac8a4 describe: limit default ref iteration to tags

This happens because every lane continues downward to the commit from where the branch was created. This pushes the commit messages off to the right, making it harder to read. Especially when the terminal screen width is reached, this becomes unusable.

Git 2.55 adds a new --graph-lane-limit=<n> option to limit the number of lanes that are drawn. Any lanes beyond the limit are replaced with a ~ truncation mark, so it stays obvious that the graph was trimmed:

git log --graph --graph-lane-limit=5

Using this option for the same 30 commits as above, we'll get:

* 26d8d94e94 A few more topics before -rc2
*   02bb39c5cb Merge branch 'js/objects-larger-than-4gb-on-windows-more'
|\
| * c6a4629e32 odb: use size_t for object_info.sizep and the size APIs
| * 7a3a78cc76 packfile,delta: drop the `cast_size_t_to_ulong()` wrappers
| * 188bac14f7 pack-objects: use size_t for in-core object sizes
| * 2d83cc3f84 packfile: widen unpack_entry()'s size out-parameter to size_t
| * 1d43315b31 pack-objects(check_pack_inflate()): use size_t instead of unsigned long
| * 33afe87338 patch-delta: use size_t for sizes
| * 8ea69373a4 compat/msvc: use _chsize_s for ftruncate
* |   8cf57cbec4 Merge branch 'kw/gitattributes-typofix'
|\ \
| * | 0bf506efd4 gitattributes: fix eol attribute for Perl scripts
* | |   8d96f09e92 Merge branch 'js/objects-larger-than-4gb-on-windows'
|\ \ \
| * | | ab3810eb6f zlib: properly clamp to uLong
* | | | 95e20213fa Hopefully final batch before -rc2
* | | |   8632b5c49d Merge branch 'en/commit-graph-timestamp-fix'
|\ \ \ \
| * | | | fbcc5408fc commit-graph: use timestamp_t for max parent generation accumulator
* | | | |   619931f561 Merge branch 'dl/posix-unused-warning-clang'
|\ \ \ \ \
| * | | | ~ cf48887610 compat/posix.h: simplify GIT_GNUC_PREREQ() comparison
| * | | | ~ ffd45926dc compat/posix.h: clean up GIT_GNUC_PREREQ() and UNUSED
| * | | | ~ 689dc92e50 compat/posix.h: enable UNUSED warning messages for Clang
* | | | | ~ 621962aa7a Merge branch 'td/ls-files-pathspec-prefilter'
|\ \ \ \ \~
| * | | | ~ 3f5203eeb4 ls-files: filter pathspec before lstat
| | |_|_|_~
| |/| | | ~
* | | | | ~ 0c706d5092 Merge branch 'ta/doc-config-adoc-fixes'
|\ \ \ \ \~
| * | | | ~ 4fa2c6e045 doc: git-config: escape erroneous highlight markup
| * | | | ~ 042221cccb doc: config/sideband: fix description list delimiter
| * | | | ~ 3eb61fda62 doc: config: terminate runaway lists
* | | | | ~ 49cb068fb2 Merge branch 'jc/t1400-fifo-cleanup'
|\ \ \ \ \~
| * | | | ~ e8f12e0e95 t1400: have fifo test clean after itself
* | | | | ~ b4970f8448 Merge branch 'td/describe-tag-iteration'
|\ \ \ \ \~
| * | | | ~ 55088ac8a4 describe: limit default ref iteration to tags

The option only makes sense together with --graph. The default is 0, which means no limit, and zero or negative values are treated the same way, just like --max-parents does.

This feature was submitted by Pablo Sabater.

Evolution of Rust in the Git codebase

In March 2025, with the release of Git 2.49, the first Rust code was added to the Git codebase. Rust bindings were added to allow Rust code to call into libgit. But none of that Rust code was used by the Git binaries.

In November 2025, in Git 2.52, the first Rust production code was introduced into Git. Then a Rust implementation for the varint subsystem was added. This code is optionally compiled if the Rust compiler is available, and when it's not, the C implementation is used. This was added as a test balloon for distributors to start preparing their tooling for a Git release that requires Rust at some point.

Earlier this year, in Version 2.54, more Rust code was added to the codebase with the introduction of the ObjectID type. This was added as part of the efforts to implement interoperability between SHA-1 and SHA-256.

Until this release, both build systems Make and Meson would gracefully fall back to the C implementation if the Rust compiler is not found. With this v2.55 release the Rust compiler is required unless you explicitly disable it in the build system.

Please note that this doesn't affect users of Git. It only affects those who build Git from source. If you compile Git and don't want to use Rust, disable it with one of these commands:

# Meson
meson configure -Drust=disabled

# Makefile
make NO_RUST=YesPlease

Bringing Rust into Git has been an ongoing (and unfinished), multi-release, community effort. It's impossible to attribute this to a single person, but some of the most prominent contributors include brian m. carlson, Patrick Steinhardt, Ezekiel Newren, and Calvin Wan.

Faster git-grep(1) and git-cherry(1) in partial clones

git-clone(1) has this feature called partial clone. This allows the user to apply a filter to what is sent over from the server. In practice, this is done with the --filter option. For example:

git clone --filter=blob:none <remote>

This will clone the repository, but that clone excludes all blobs (i.e. the contents of the files in the tree). This can speed up the clone tremendously, but it comes at the cost that Git needs to download blobs later when other commands are used that read file contents. And some commands might need a lot of missing blobs.

git-grep(1) is one of those commands, as it searches the content of the files. To do so, it obviously needs to have those files. Imagine you want to search the word "TODO" 100 commits back in history:

git grep TODO HEAD~100

This command resolves the HEAD~100 commit and the trees associated with that. But those trees might point to blobs that aren't downloaded yet. Previously, each blob was downloaded separately. But that is improved in Git 2.55. In this version of Git, the blob downloads are batched together into a single negotiating round-trip with the server.

This batching is now implemented for both git-grep(1) and git-cherry(1).

This change was submitted by Elijah Newren.

Read more

This article highlighted just a few of the contributions made by GitLab and the wider Git community for this latest release. You can learn about these from the official release announcement of the Git project. Also, check out our previous Git release blog posts to see other past highlights of contributions from GitLab team members.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories