WP_Query Object
(
[query] => Array
(
[post_type] => post
[showposts] => 8
[orderby] => Array
(
[date] => desc
) [autosort] => 0
[paged] => 0
[post__not_in] => Array
(
[0] => 5233
) ) [query_vars] => Array
(
[post_type] => post
[showposts] => 8
[orderby] => Array
(
[date] => desc
) [autosort] => 0
[paged] => 0
[post__not_in] => Array
(
[0] => 5233
) [error] =>
[m] =>
[p] => 0
[post_parent] =>
[subpost] =>
[subpost_id] =>
[attachment] =>
[attachment_id] => 0
[name] =>
[pagename] =>
[page_id] => 0
[second] =>
[minute] =>
[hour] =>
[day] => 0
[monthnum] => 0
[year] => 0
[w] => 0
[category_name] =>
[tag] =>
[cat] =>
[tag_id] =>
[author] =>
[author_name] =>
[feed] =>
[tb] =>
[meta_key] =>
[meta_value] =>
[preview] =>
[s] =>
[sentence] =>
[title] =>
[fields] => all
[menu_order] =>
[embed] =>
[category__in] => Array
(
) [category__not_in] => Array
(
) [category__and] => Array
(
) [post__in] => Array
(
) [post_name__in] => Array
(
) [tag__in] => Array
(
) [tag__not_in] => Array
(
) [tag__and] => Array
(
) [tag_slug__in] => Array
(
) [tag_slug__and] => Array
(
) [post_parent__in] => Array
(
) [post_parent__not_in] => Array
(
) [author__in] => Array
(
) [author__not_in] => Array
(
) [search_columns] => Array
(
) [ignore_sticky_posts] =>
[suppress_filters] =>
[cache_results] => 1
[update_post_term_cache] => 1
[update_menu_item_cache] =>
[lazy_load_term_meta] => 1
[update_post_meta_cache] => 1
[posts_per_page] => 8
[nopaging] =>
[comments_per_page] => 50
[no_found_rows] =>
[order] => DESC
) [tax_query] => WP_Tax_Query Object
(
[queries] => Array
(
) [relation] => AND
[table_aliases:protected] => Array
(
) [queried_terms] => Array
(
) [primary_table] => wp_posts
[primary_id_column] => ID
) [meta_query] => WP_Meta_Query Object
(
[queries] => Array
(
) [relation] =>
[meta_table] =>
[meta_id_column] =>
[primary_table] =>
[primary_id_column] =>
[table_aliases:protected] => Array
(
) [clauses:protected] => Array
(
) [has_or_relation:protected] =>
) [date_query] =>
[request] => SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
WHERE 1=1 AND wp_posts.ID NOT IN (5233) AND ((wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'expired' OR wp_posts.post_status = 'acf-disabled' OR wp_posts.post_status = 'tribe-ea-success' OR wp_posts.post_status = 'tribe-ea-failed' OR wp_posts.post_status = 'tribe-ea-schedule' OR wp_posts.post_status = 'tribe-ea-pending' OR wp_posts.post_status = 'tribe-ea-draft')))
ORDER BY wp_posts.post_date DESC
LIMIT 0, 8
[posts] => Array
(
[0] => WP_Post Object
(
[ID] => 5294
[post_author] => 15
[post_date] => 2026-08-27 20:09:42
[post_date_gmt] => 2026-08-27 20:09:42
[post_content] => Have you ever lost work because of an error done while using Git? Perhaps by deleting an unpushed branch by mistake, or having done a hard reset on the wrong repository? A destructive error could mean hours of reconstruction, unless you’re aware that Git has a feature meant for recovery from this sort of mistake. This feature is known as the reference log, or "reflog" for short. The reflog is your local record of where your branch pointer is, and where it has been in recent history. Every time a change to a branch pointer happens, Git remembers the position. It should be noted that the reflog is local to specific clones. Short of packing up the .git folder and sending it to a colleague, doing a recovery via the reflog needs to be done on the copy that the error was done on. So, what exactly does the reflog track, and what does it not? As mentioned, branch pointers are tracked, but so are remote tracking branch updates, HEAD movements and stashes. A list of common operations this includes are:
- clone (will be the initial state until it falls out of history)
- pull (a merge done as a part of a pull step will add an additional entry)
- commit (including separate instances of --amend)
- checkout
- stash
- merge
- rebase
- reset
Most importantly, what the reflog does
not track are changes that were never staged or committed. The key takeaway for this being: stage/commit your code often; don’t allow large monolithic deltas to catch you off-guard.
Basic Operations
Interacting with the reference log is done via the `git reflog` command. Here is an example reflog output:
ebe1ad46f (HEAD -> dev, origin/dev) HEAD@{0}: commit: Build and Run on MacOS
3fb7c8ff4 HEAD@{1}: pull: Fast-forward
f9d877784 HEAD@{2}: commit: Fixing contrib dead linkage
372bbea2b HEAD@{3}: reset: moving to origin/dev
372bbea2b HEAD@{4}: checkout: moving from master to dev
3b6a7e870 (origin/master, origin/HEAD, master) HEAD@{5}: clone: from <repo URL>Each entry shows, in order:
- The commit hash that the reference points to
- The reference name (e.g. this case "HEAD", "dev" [local branch], "origin/dev" [remote branch])
- Position information within curly braces (e.g. {4}) indicating how many steps back
- Action with git command used to cause the change, and a brief description of the change
Reference logs for specific branches can also be done:
$ git reflog show master
3b6a7e870 (origin/master, origin/HEAD, master) master@{0}: clone: from <repo URL>As well as timestamps:
$ git reflog show master --relative-date # Also try --date=iso
3b6a7e870 (origin/master, origin/HEAD, master) master@{7 weeks ago}: clone: from <repo URL>And even with patches displayed as part of the output:
$ git reflog show master --relative-date -p
3b6a7e870 (origin/master, origin/HEAD, master) master@{7 weeks ago}: clone: from <repo URL>
diff --git a/src/app/app.cpp b/src/app/app.cpp
index 7833fae15..6da32d596 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -33,7 +33,7 @@ static void SetCompleted(App *_app, bool &_) { App::s_isCompleted = _isCompleted; }
-App::Model *App::s_model = save::g_appFormatter.RegisterModel<App>(
+App::Model *App::s_model = save::ModelStorage::RegisterModel<App>(
APP_STRINGIFY(App),
save::AttributeAccess<&App::m_loadedSavefileVer,Common scenarios
Now that we have some understanding of what we’re looking at with the reference log output, let’s go over some scenarios. Example 1: Accidental use of `reset --hard`:
$ git reset --hard HEAD~3
# Oops.
$ git reflog
8a91155e0 (HEAD -> dev) HEAD@{0}: reset: moving to HEAD~3
ebe1ad46f (origin/dev) HEAD@{1}: commit: Build and Run on MacOS
[...]
# Now we use the commit before the reset (ebe1ad46f HEAD@{1})
$ git reset --hard ebe1ad46f # using "HEAD@{1}" in place of the hash is also acceptable
And we have effectively undone a hard reset. Note that in doing this, the first hard reset (the 8a91155e0 hash) is still present in the reference log and is now the new HEAD@{1}.
Example 2: Lost commit from a rebase
$ git rebase -i HEAD~5
Successfully rebased and updated refs/heads/dev.
# We need that last commit, however...
$ git reflog
0d9cae3c1 (HEAD -> dev) HEAD@{0}: rebase (finish): returning to refs/heads/dev
0d9cae3c1 (HEAD -> dev) HEAD@{1}: rebase (start): checkout HEAD~5
ebe1ad46f (origin/dev) HEAD@{2}: commit: Build and Run on MacOS
# Now we can cherry-pick the hash of the needed commit
$ git cherry-pick ebe1ad46f
# Don’t forget to commit!Example 3: Mistakenly deleted branch
$ git checkout master
M .gitignore
M .gitmodules
[...]
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
$ git branch -D dev
Deleted branch dev.
# Oops again.
$ git reflog
3853c23ba (HEAD -> master, origin/master) HEAD@{0}: checkout: moving from dev to master
ebe1ad46f (origin/dev) HEAD@{1}: commit: Build and Run on MacOS
[...]
# We can recreate the branch in whole from the last state we saw that branch in
$ git checkout -b dev ebe1ad46f
M .gitignore
M .gitmodules
[...]
Switched to a new branch 'dev'One Final Note
It is important to know that the reference log does not, by default, hold all history forever. Depending on the situation, entries will expire within 30 or 90 days. This behaviour can be changed via:
# You can also use "never" to keep everything or "now" to not keep a log (strongly discouraged)
# You can also make this affect all repos you use with --global
$ git config gc.reflogExpire 365.days
$ git config gc.reflogExpireUnreachable 90.days
Ultimately, the reference log serves well as a light in the dark, should things go wrong. These are some common examples, but most destructive operations short of deleting the .git folder can be recovered, from bad amends on commits to merges gone awry.
References and Additional Reading:
[table id=11 /]
[post_title] => Gitting Good – Leveraging the Reference Log For Disaster Recovery
[post_excerpt] =>
[post_status] => publish
[comment_status] => closed
[ping_status] => closed
[post_password] =>
[post_name] => gitting-good-leveraging-the-reference-log-for-disaster-recovery
[to_ping] =>
[pinged] =>
[post_modified] => 2026-08-27 20:10:16
[post_modified_gmt] => 2026-08-27 20:10:16
[post_content_filtered] =>
[post_parent] => 0
[guid] => https://keyvatech.com/?p=5294
[menu_order] => 0
[post_type] => post
[post_mime_type] =>
[comment_count] => 0
[filter] => raw
) [1] => WP_Post Object
(
[ID] => 5289
[post_author] => 15
[post_date] => 2026-08-24 14:39:26
[post_date_gmt] => 2026-08-24 14:39:26
[post_content] => Have you ever worked on a project that required some sort of third-party dependency, and didn’t have the luxury of a mature package manager such as Python’s pip, JavaScript's npm or Rust’s cargo? Whatever the reason, be it use of a language that doesn’t have a standard package manager such as C++, or a specific internal repo not available as a package, Git has a feature to ease the pain of managing these external dependencies, known as submodules. At the core, the submodule system is a means of importing a specific commit into a project. These submodules are managed separately of the rest of the repository, and the only data pushed to your project is the information on how to fetch the submodule, rather than pushing the entire contents of a needed dependency.
Adding a submodule
Before adding a submodule to a project, consider mirroring the target repository to your own Git instance. If a target repository server goes down, then a fresh pull of your project will not be able to receive this code without some effort (see
Gitting Good - How to Collaborate Without a Central Service for sample workarounds), so it’s better to be prepared ahead of time by having a regularly syncing mirror repository. Once this has been done, you can add your dependency as its only directory by using the git submodule feature:
# Adding a submodule at the default (master/main) branch’s HEAD
git submodule add https://git.keyva.internal/externals/openssl.git contribs/openssl
# Or, using '-b' to declare a specific branch to use
git submodule add -b openssl-3.6 https://git.keyva.internal/externals/openssl.git contribs/openssl
A new file named .gitmodules will be added to your project root the first time a submodule is added:
[submodule "contribs/openssl"]
path = contribs/openssl
url = gitea@https://git.keyva.internal/externals/openssl.git
branch = openssl-3.6
An entry in .git/config will also be created:
[submodule "contribs/openssl"]
active = true
url = gitea@https://git.keyva.internal/externals/openssl.git
Whenever a new submodule is added, the .gitmodules file should be committed to track that change. No other objects need to be added to the index.
Pulling submodules within a project
Cloning a project does not automatically also clone submodules within that project. Cloning submodules can be done in two main ways:
# At the time of cloning:
git clone --recurse-submodules https://git.keyva.internal/projects/test-app.git
# Or, if the project is already cloned:
git submodule update --init --recursive
The --recursive flag is used to handle nested submodules; that is to say: submodules of submodules.
Updating submodules
It is important to note that submodules track a specific commit, rather than a branch, though defining a branch as above will tell Git what branch the desired commit is in. For this reason, new updates to a submodule’s project are not automatically pulled in. The purpose of this is in part to ensure that a project’s state, that is to say each commit, remains consistent. Should it be needed to roll back to an earlier commit to check on prior behavior, that commit will point to the same commit of the submodules as when it was originally authored. Because of this, submodule updates are done with a different step from pulling the main repository:
# Update everything recursively
git submodule update --remote –recursive
# Commit the new updates after testing
git add contribs/*
Git commit –m "Update submodules"
Removing submodules
For the same reasons that updating submodules is a seperate process from the main repository, removing submodules also requires a different process to be "clean":
# Deinitialize
git submodule deinit -f -- contribs/openssl
# Remove from index and working tree
git rm -f contribs/openssl
# Remove metadata
rm -rf .git/modules/contribs/openssl
# Commit
git add .gitmodules
git commit -m "Removing openssl submodule"
Common issues
When within a submodule directory, you exist in what is effectively that submodule's project, and within a detached HEAD state of that project. If changes need to be made, a branch reference must be defined first:
# Get to the project directoy
cd contrib/openssl
# Checkout an existing branch
git checkout main
# Or create a new one
git checkout –b keyva-dev
# Do whatever changes are needed, and then:
git push origin keyva-dev # Still in the submodule's directory
# Move back to main project directory
cd ../..
# Update the pointer for the submodule
git add contrib/openssl
# And then finally commit
git commit -m "Update submodule to latest"
Another somewhat common issue is changing the repository URL. This can be because a project owner changed where their repository is hosted, or maybe because you started with an external reository and now what to move to an internal, mirrored one. URLs can be updated first by editing the relevant area in .gitmodules, followed by running git submodule sync, which will update .git/config. Add .gitmodules to the index and commit once tested.
Common uses
As previously mentioned, the primary use case for submodules is importing external code to a project. However, this also makes it a good case for avoiding code reuse and drift. For instance, one C++ project I use internally handles common features such as architecture-specific handling, string utilities and special math handling. This project is used by all other end projects and, as a result of being included as a submodule, the code for it has a single source of truth. This means that code isn’t needlessly duplicated across projects, and behavior doesn’t drift between projects because one feature exists in one project but not others. Use of submodules is also a decent way to anchor commits. If a submodule’s repository frequently pushes breaking changes, declaring a specific commit as the one to be using is an easy solution, allowing updating to these submodules at one’s own pace, rather than being made to adjust to changes in the middle of another workflow. [table id=11 /]
[post_title] => Gitting Good - Using Submodules to Manage Dependencies
[post_excerpt] =>
[post_status] => publish
[comment_status] => closed
[ping_status] => closed
[post_password] =>
[post_name] => gitting-good-using-submodules-to-manage-dependencies
[to_ping] =>
[pinged] =>
[post_modified] => 2026-08-24 14:39:26
[post_modified_gmt] => 2026-08-24 14:39:26
[post_content_filtered] =>
[post_parent] => 0
[guid] => https://keyvatech.com/?p=5289
[menu_order] => 0
[post_type] => post
[post_mime_type] =>
[comment_count] => 0
[filter] => raw
) [2] => WP_Post Object
(
[ID] => 5281
[post_author] => 15
[post_date] => 2026-08-13 13:22:46
[post_date_gmt] => 2026-08-13 13:22:46
[post_content] => Git has a feature to help hunt down bugs for a very specific sort of problem: when you have a regression, but you don't know when it was introduced. This feature, known as `bisect`, can assist is tracking down such an issue by running a binary search through a repository's history to find the exact commit that such a regression was introduced.
Basic setup
# To initialize
git bisect start
# Declaring the current commit as broken; a tag or commit hash can also be included if an earlier broken commit is already known
git bisect bad
# Declaring a specific tag as a working commit; specific commit hashes may again be used
git bisect good v1.2.0-release
At this point, Git will check out the commit at the midway point between the latest known working and earlier known not-working commits. From here, it is a simple matter to test the checked out commit and:
# If the checkout has the bug:
git bisect bad
# Or if it does not:
git bisect good
# Or, if there is something preventing testing with this specific commit:
git bisect skip
Every time a commit is determined to be good or bad, Git will automatically checkout the next "middle" commit to continue the process. In doing this, one can track down the commit that introduced the bug in O(log
2(n)) steps, in addition to any skips done in the process. That means even with a thousand commits, it won't take more than 10 steps to reach an answer. Ten thousand commits? Not more than 14 steps.
Workflow Considerations
The bisect feature is at its best when commits are atomic; that is to say small and focused changes that affect one logical area at a time. One bug fix, or one feature, or one refactorization are all considered atomic. Bisect results are most useful when they land on a small commit, as a small diff will make it clearer as to why an undesirable change has happened. Squash commits should also be avoided for this reason, as they remove history and make multiple smaller commits into singular large ones. It’s also a good idea to not commit work-in-progress commits, or commits that don't pass needed tests. This is not always possible to avoid depending on the environment. If this is the case, it would be wise to start commit messages by saying so, e.g. with "[WIP]" or something equivalent, to indicate that the skip command should be used immediately.
Automation
Good practices with workflow can also allow for ease of automation. Having a test suite that can determine if a commit is good or not can assist greatly. This can be done via exit codes:
- 0 for a successful run (indicating good)
- A value between 1 and 124 or 126 or 127 for a failed test (indicating bad)
- A value of 125 if a build failed (indicating skip)
- Any other value to abort the bisect process
This test need not be a part of the repo itself, so any new testing that needs to be done won't need to interfere with multiple checkouts. To start automated tests, we begin in much the same way:
git bisect start
git bisect bad
git bisect good v1.2.0-release
# To declare how to test automatically:
git bisect run ~/tests/run-test.sh # or wherever your script is
Tests can be done inline as well. This is useful for many functions, with perhaps the best example being scanning output for a specific message or message fragment:
git bisect run bash -c '/usr/bin/env python3 main.py arg1 arg2 | grep -q "ERROR"'
Finally, using worktree, a specific current and committed test suite can also be defined, allowing checkouts of running code while leaving the most current tests intact:
git worktree add ../bisect-tests HEAD
git bisect run ../bisect-tests/tests/run.sh
Other uses
Finding the point of origin of bugs isn't the end of utility for Git bisect. Using other available scripts, one can use bisect to find performance issues. Some additional things that can be done include:
- Finding when a performance issue began
- Finding when a function or feature was added to the repo (with run grep -q)
- Finding when CI pipelines began failing
- Finding when a security vulnerably was introduced
- Detecting when code coverage degraded
Bisect works well as a general purpose tool for determining when and where a change happened. The only requirement is that it is something testable with a script that can return a pass/fail response. Official Documentation:
https://git-scm.com/docs/git-bisect. [table id=11 /]
[post_title] => Gitting Good - Using Bisect - Git's Built-in Debugging Tool
[post_excerpt] =>
[post_status] => publish
[comment_status] => closed
[ping_status] => closed
[post_password] =>
[post_name] => gitting-good-using-bisect-gits-built-in-debugging-tool
[to_ping] =>
[pinged] =>
[post_modified] => 2026-08-13 14:06:04
[post_modified_gmt] => 2026-08-13 14:06:04
[post_content_filtered] =>
[post_parent] => 0
[guid] => https://keyvatech.com/?p=5281
[menu_order] => 0
[post_type] => post
[post_mime_type] =>
[comment_count] => 0
[filter] => raw
) [3] => WP_Post Object
(
[ID] => 5268
[post_author] => 7
[post_date] => 2026-06-30 14:09:24
[post_date_gmt] => 2026-06-30 14:09:24
[post_content] => Perhaps you are testing across multiple machines and don't want to push incomplete code. Maybe you are working with a colleague on a particularly long flight. Or perhaps your main repository has become unreachable, and you don't know when it will be back. Thankfully, Git has solutions for these times, without needing additional software or configuration to work with. At its core, Git is designed to not require a central service or authority to work. This might come as something of a surprise, since most projects use a central authority such as GitHub, GitLab or BitBucket. However, within your pulled repository exists a complete history of your pulled branch's changes, from the very first commit. Because of this, any machine can act as a Git server. This can be leveraged even with the general internet unavailable; no special services or daemons are required. Not even a reliable network connection is required, so long as you have a USB drive handy. All that you need between two computers is both having Git, and both able to communicate in some way.
Option 1 - Bare repo on shared media
Those of us who collaborated with others in a pre-internet world know the usefulness of sneakernet. For the rest who are unaware, this is basically comprised of adding your data to media storage (these days, probably a USB drive) and physically walking it over to where it needs to be. When services are down, it remains the most consistently reliable workaround. To use this method with Git, first we initialize a bare repo on our media:
cd /media/usb
git init --bare repo.git
Then, we can add this path as a remote to push to:
cd ~/src/repo
git remote add usb-stick /media/usb/repo.git
git push usb-stick main
Finally, we unmount the USB and bring it to the target machine and run the same setup before pulling:
cd ~/src/repo
git remote add usb-stick /media/usb/repo.git
git pull usb-stick main
Now, in order to sync later, the chosen media can be passed back and forth as your temporary central repository. If you only need to pass a repository and not later share history, an even simpler cudgel is available. With the above example, `/media/usb/repo.git` is effectively a copy of `~/src/repo/.git`. It would also be possible to simply:
cp -r ~/src/repo/.git /media/usb/repo.git
Then, on the target machine:
mkdir ~/src/repo
~/src/repo/
cp -r /media/usb/repo.git ~/src/repo/.git
git reset --hard
At this point, you have a carbon copy of the history and a working source directory. Perhaps even a bit better than the prior solution for initial sharing, because this method would share all branches on the source machine as well, instead of needing to individually push the same branches.
Option 2 - Direct peer-to-peer over local IP
It's also possible with a bridge, hotspot, or even just direct connection to do this over a two terminal network. In this example, we will assume that the source computer has an IP of 192.168.1.10 on this network. It's important to preface this by stating: don't do this outside of an isolated network, as this makes your served repo readable to anyone on your network. It's the sort of solution meant for long car rides and international flights, not general use. On our source machine:
# Make our service path
mkdir -p /srv/git && cd /srv/git
# Init a bare repo
git init --bare repo.git
# Add this local repo to your remotes; like the USB method, we can simply use the path
cd ~/src/repo
git remote add local /srv/git/repo.git
git push local main
# Return to the service path and launch the git daemon
cd /srv/git
git daemon --base-path=. --export-all --reuseaddr --verbose --port=9418
This invokes a lightweight, read-only service daemon that is built into Git, running on Git's default port of 9418. It requires no authentication, which is fine for our very small, isolated network. Then, our target can clone directly from this repository:
cd ~/src
git clone git://192.168.1.10/repo.git
Best practices would have both machines running their own daemon, committing and pushing to their local path and each machine pulling from each other. If it's more desirable however, one can also have our source machine act as a central authority. Again, not to be used outside an isolated network, and with additional peril, that this method would allow not only readability to everything on the network, but writability as well.
# Before running `git daemon`:
cd /srv/git/repo.git
git config receive.denyCurrentBranch ignore # `warn` is also an option in place of `ignore`
cd /srv/git
# Note the addition of --enable=receive-pack
git daemon --base-path=. --export-all --reuseaddr --verbose --port=9418 --enable=receive-pack
Option 3 - With SSH transport (most realistic for an office or VPN setting)
If sshd is available, changes can be pushed directly using SSH without running any daemon. Note for MacOS: enable Remote Login in System Settings -> General -> Sharing -> Advanced -> Remote Login
For Linux: systemctl start sshd (assuming the sshd package is available)
For Windows: You will require WSL, then installing the relevant package (most distros use the package openssh-server) and then port forwarding. This is beyond the scope of this guide, but it should be searchable, knowing what to look for. This will also likely require permissions handling, also beyond the scope of this guide. For Linux, search for how to add users and ssh public keys and group permissions with chmod or setfacl. As before, we will set up a directory for our repo and push to it normally:
# Make our service path
mkdir -p /srv/git && cd /srv/git
# Init a bare repo
git init --bare repo.git
# Add this local repo to your remotes; like the USB method, we can simply use the path
cd ~/src/repo
git remote add local /srv/git/repo.git
git push local main
Then, for our target machine, we can clone directly as if a path over SSH:
git remote add machine1 ssh://user@192.168.1.10/srv/git/repo.git
git pull machine1 main
If you wish to learn about a more retro way of creating patch files (the method of choice for collaboration over email), I suggest looking into the official documentation:
[table id=11 /]
[post_title] => Gitting Good - How to Collaborate Without a Central Service
[post_excerpt] =>
[post_status] => publish
[comment_status] => closed
[ping_status] => closed
[post_password] =>
[post_name] => gitting-good-how-to-colabborate-without-a-central-service
[to_ping] =>
[pinged] =>
[post_modified] => 2026-08-13 14:10:48
[post_modified_gmt] => 2026-08-13 14:10:48
[post_content_filtered] =>
[post_parent] => 0
[guid] => https://keyvatech.com/?p=5268
[menu_order] => 0
[post_type] => post
[post_mime_type] =>
[comment_count] => 0
[filter] => raw
) [4] => WP_Post Object
(
[ID] => 5264
[post_author] => 7
[post_date] => 2026-06-25 16:57:23
[post_date_gmt] => 2026-06-25 16:57:23
[post_content] => There's a version of AI adoption in IT ops that looks something like this: someone buys a Copilot license, attaches it to ServiceNow, and waits for the magic to happen. Three months later, the tickets are still messy, the on-call engineer is still waking up at 2 am, and nobody can explain why the AI recommended closing an incident that was still actively on fire. We've seen this pattern more times than we can count. And it's not a technology problem, it's an approach problem.
The Gap Between "AI-Capable" and "AI-Useful" Infrastructure and operations teams aren't short on tools. Most have 150% of the tools they need. They have observability stacks, ITSM platforms, runbook documentation, and CI/CD pipelines generating telemetry constantly. The problem is that nobody has connected those data sources to an AI workflow in a way that actually reflects how the team works. One of our clients - a regional financial services firm - managing roughly 400 VMs across a hybrid AWS and on-prem footprint came to us after spending six months trying to get Azure OpenAI to summarize their PagerDuty alerts in a useful way. The summaries were technically accurate but operationally useless. They'd describe
what happened with perfect clarity and say nothing about the
why, and nothing about what the on-call engineer should do about it at midnight. The root cause wasn't the model. It was that nobody had connected the alert data to the runbooks, to the CMDB topology, or to the historical incident record, or the knowledge base. The AI was operating without context like asking a new hire to triage production issues on their first day without showing them the architecture diagram.
What The Three Days of Workshop Actually Looks Like Our AI Enablement Workshop is a three-day, hands-on engagement. Not a training. Not a slide deck. We come in, work directly with your tools and your data, and leave with working prototypes and a prioritized roadmap.
Day one is mostly listening. We sit with the platform engineering team, the SRE leads, and often someone from security. We ask a lot of questions about where the pain actually lives - not the official answer, but the real one. Alert fatigue is almost always on the list. So is the runbook problem. Most teams have documentation that's six months out of date (sometimes years out of date) and written in a way that only the person who wrote it can interpret quickly under pressure. We also audit data readiness. This is where most AI projects quietly fall apart. A Tier 2 incident with three lines of description and no attached logs is not a useful training signal. We look at what's actually in your ITSM system, what observability data can be queried, and whether the runbooks are in a format that a retrieval-augmented AI can actually use.
Day two is where we build. Typically, we develop two or three use cases. The specific mix depends on what the team needs most. The most common starting point is an Incident Copilot: an AI-powered assistant that, when a P1 fires, automatically pulls the relevant alerts, correlates them with recent change activity, surfaces the most applicable runbook section, and produces a structured incident summary that goes directly into the ticket. For one manufacturing client, this took the average time-to-context – i.e. the point at which the on-call engineer actually understands what they're dealing with, from 18 minutes down to under 4. That's not a vendor benchmark. That's a number their SRE lead pulled from incident records after 4 weeks in production. We also commonly build a Runbook Assistant - a natural language interface over internal operational documentation. Engineers type a question in plain English, like "what do we do when the payments service is throwing 503s from the ALB?" And get a synthesized, sourced answer from the actual runbooks, not a hallucinated guess. The key engineering challenge here is retrieval quality and chunking strategy. Getting that wrong is what produces the confident-sounding wrong answers that erode trust in AI tools. Another key point to take away is that developing habits for optimizing tokens will help with making these tools cost efficient.
Day three is architecture and roadmap. Where does this go in production? How does it connect to Teams or Slack for alert delivery? What are the human approval controls? What does the security and governance boundary look like, and who owns the ongoing prompt maintenance?
Frankly Speaking Not every team is ready for day two on day one. We've walked into environments where the runbooks are in a SharePoint folder that hasn't been touched in two years, or where the observability data is rich but structured in a way that requires significant preprocessing before it's useful as AI context. That's fine, since part of what the workshop produces is clarity on what has to happen before scaling, not just what can be built right now. We also don't recommend automating things that shouldn't be automated yet. Ticket enrichment – i.e. automatically classifying, tagging, and augmenting incoming tickets before a human reviews them - is a very different risk profile from automated ticket
resolution. We help teams think through that boundary clearly, and we're direct when we think a client is moving faster than their governance posture supports.
What You Walk Away With After three days, teams have working prototypes built against their own data, a reusable codebase and prompt library, an architecture blueprint for production deployment, and a 30/60/90-day roadmap with use cases prioritized by effort versus operational impact. More practically, they have a team that has actually built something with AI, and not just watched a demo. They have a concrete sense of what AI can and cannot do in their environment. That last part matters more than people think. The biggest barrier to AI adoption in infrastructure operations isn't budget or tooling. It's the lack of a shared mental model across the team for what AI is actually good at and how to work with it. The workshop builds that model by doing, not by describing.
Keyva delivers the AI Enablement Workshop for Infrastructure and Operations as a fixed-price, three-day engagement. If you're evaluating where AI can have the most immediate impact for your ops team, reach out at info@keyvatech.com. [table id=3 /]
[post_title] => When AI Actually Works in IT Operations — And Why Most Teams Are Doing It Wrong
[post_excerpt] =>
[post_status] => publish
[comment_status] => closed
[ping_status] => closed
[post_password] =>
[post_name] => when-ai-actually-works-in-it-operations-and-why-most-teams-are-doing-it-wrong
[to_ping] =>
[pinged] =>
[post_modified] => 2026-06-25 16:57:23
[post_modified_gmt] => 2026-06-25 16:57:23
[post_content_filtered] =>
[post_parent] => 0
[guid] => https://keyvatech.com/?p=5264
[menu_order] => 0
[post_type] => post
[post_mime_type] =>
[comment_count] => 0
[filter] => raw
) [5] => WP_Post Object
(
[ID] => 5257
[post_author] => 7
[post_date] => 2026-06-11 09:37:02
[post_date_gmt] => 2026-06-11 09:37:02
[post_content] =>
Keyva is pleased to announce the certification of the Keyva BMC Atrium Data Pump v2.0.1 for the new ServiceNow Zurich release. Clients can now seamlessly upgrade their ServiceNow App from previous ServiceNow releases (Yokohama, Xanadu) to the Zurich release.
The ServiceNow Zurich release delivers enhanced AI-driven workflows, improved user experiences, and expanded automation capabilities to increase productivity, resilience, and service efficiency across the enterprise.
Keyva's BMC Atrium Data Pump v2.0.1 provides synchronization of CIs, CI attributes, and relationships from the ServiceNow CMDB into the BMC Atrium CMDB. This integration allows organizations to leverage their existing investment in Enterprise Software and avoid costly "Rip and Replace" projects.
Learn more about the Keyva BMC Atrium Data Pump and view all the ServiceNow releases for which Keyva has been certified at the ServiceNow Store: store.servicenow.com.
[post_title] => Keyva BMC Atrium Data Pump Certified for Zurich Release
[post_excerpt] =>
[post_status] => publish
[comment_status] => closed
[ping_status] => closed
[post_password] =>
[post_name] => keyva-bmc-atrium-data-pump-servicenow-to-bmc-certified-for-zurich-release
[to_ping] =>
[pinged] =>
[post_modified] => 2026-06-10 20:37:23
[post_modified_gmt] => 2026-06-10 20:37:23
[post_content_filtered] =>
[post_parent] => 0
[guid] => https://keyvatech.com/?p=5257
[menu_order] => 0
[post_type] => post
[post_mime_type] =>
[comment_count] => 0
[filter] => raw
) [6] => WP_Post Object
(
[ID] => 5258
[post_author] => 15
[post_date] => 2026-06-10 20:36:53
[post_date_gmt] => 2026-06-10 20:36:53
[post_content] =>
Keyva is pleased to announce the certification of the Keyva HP Universal CMDB Data Pump for the new ServiceNow Zurich release. Clients can now seamlessly upgrade their ServiceNow App from previous ServiceNow releases (Yokohama, Xanadu) to the Zurich release.
The ServiceNow Zurich release delivers enhanced AI-driven workflows, improved user experiences, and expanded automation capabilities to increase productivity, resilience, and service efficiency across the enterprise.
Keyva's HP Universal CMDB Data Pump provides synchronization of CIs, CI attributes, and relationships between the ServiceNow CMDB and the HP Universal CMDB. This integration allows organizations to leverage their existing investment in Enterprise Software and avoid costly "Rip and Replace" projects.
Learn more about the Keyva HP Universal CMDB Data Pump and view all the ServiceNow releases for which Keyva has been certified at the ServiceNow Store: store.servicenow.com.
[post_title] => Keyva HP Universal CMDB Data Pump Certified for Zurich Release
[post_excerpt] =>
[post_status] => publish
[comment_status] => closed
[ping_status] => closed
[post_password] =>
[post_name] => keyva-hp-universal-cmdb-data-pump-certified-for-zurich-release
[to_ping] =>
[pinged] =>
[post_modified] => 2026-06-10 20:36:53
[post_modified_gmt] => 2026-06-10 20:36:53
[post_content_filtered] =>
[post_parent] => 0
[guid] => https://keyvatech.com/?p=5258
[menu_order] => 0
[post_type] => post
[post_mime_type] =>
[comment_count] => 0
[filter] => raw
) [7] => WP_Post Object
(
[ID] => 5256
[post_author] => 7
[post_date] => 2026-06-09 02:03:13
[post_date_gmt] => 2026-06-09 02:03:13
[post_content] =>
Keyva is pleased to announce the certification of the Keyva BMC Atrium Data Pump v3.2.1 for the new ServiceNow Zurich release. Clients can now seamlessly upgrade their ServiceNow App from previous ServiceNow releases (Yokohama, Xanadu) to the Zurich release.
The ServiceNow Zurich release delivers enhanced AI-driven workflows, improved user experiences, and expanded automation capabilities to increase productivity, resilience, and service efficiency across the enterprise.
Keyva's BMC Atrium Data Pump v3.2.1 provides synchronization of CIs, CI attributes, and relationships from the BMC Atrium CMDB into the ServiceNow CMDB. This integration allows organizations to leverage their existing investment in Enterprise Software and avoid costly "Rip and Replace" projects.
Learn more about the Keyva BMC Atrium Data Pump and view all the ServiceNow releases for which Keyva has been certified at the ServiceNow Store: store.servicenow.com.
[post_title] => Keyva BMC Atrium Data Pump Certified for Zurich Release
[post_excerpt] =>
[post_status] => publish
[comment_status] => closed
[ping_status] => closed
[post_password] =>
[post_name] => keyva-bmc-atrium-data-pump-bmc-to-servicenow-certified-for-zurich-release
[to_ping] =>
[pinged] =>
[post_modified] => 2026-06-09 02:03:13
[post_modified_gmt] => 2026-06-09 02:03:13
[post_content_filtered] =>
[post_parent] => 0
[guid] => https://keyvatech.com/?p=5256
[menu_order] => 0
[post_type] => post
[post_mime_type] =>
[comment_count] => 0
[filter] => raw
) ) [post_count] => 8
[current_post] => -1
[before_loop] => 1
[in_the_loop] =>
[post] => WP_Post Object
(
[ID] => 5294
[post_author] => 15
[post_date] => 2026-08-27 20:09:42
[post_date_gmt] => 2026-08-27 20:09:42
[post_content] => Have you ever lost work because of an error done while using Git? Perhaps by deleting an unpushed branch by mistake, or having done a hard reset on the wrong repository? A destructive error could mean hours of reconstruction, unless you’re aware that Git has a feature meant for recovery from this sort of mistake. This feature is known as the reference log, or "reflog" for short. The reflog is your local record of where your branch pointer is, and where it has been in recent history. Every time a change to a branch pointer happens, Git remembers the position. It should be noted that the reflog is local to specific clones. Short of packing up the .git folder and sending it to a colleague, doing a recovery via the reflog needs to be done on the copy that the error was done on. So, what exactly does the reflog track, and what does it not? As mentioned, branch pointers are tracked, but so are remote tracking branch updates, HEAD movements and stashes. A list of common operations this includes are:
- clone (will be the initial state until it falls out of history)
- pull (a merge done as a part of a pull step will add an additional entry)
- commit (including separate instances of --amend)
- checkout
- stash
- merge
- rebase
- reset
Most importantly, what the reflog does
not track are changes that were never staged or committed. The key takeaway for this being: stage/commit your code often; don’t allow large monolithic deltas to catch you off-guard.
Basic Operations
Interacting with the reference log is done via the `git reflog` command. Here is an example reflog output:
ebe1ad46f (HEAD -> dev, origin/dev) HEAD@{0}: commit: Build and Run on MacOS
3fb7c8ff4 HEAD@{1}: pull: Fast-forward
f9d877784 HEAD@{2}: commit: Fixing contrib dead linkage
372bbea2b HEAD@{3}: reset: moving to origin/dev
372bbea2b HEAD@{4}: checkout: moving from master to dev
3b6a7e870 (origin/master, origin/HEAD, master) HEAD@{5}: clone: from <repo URL>Each entry shows, in order:
- The commit hash that the reference points to
- The reference name (e.g. this case "HEAD", "dev" [local branch], "origin/dev" [remote branch])
- Position information within curly braces (e.g. {4}) indicating how many steps back
- Action with git command used to cause the change, and a brief description of the change
Reference logs for specific branches can also be done:
$ git reflog show master
3b6a7e870 (origin/master, origin/HEAD, master) master@{0}: clone: from <repo URL>As well as timestamps:
$ git reflog show master --relative-date # Also try --date=iso
3b6a7e870 (origin/master, origin/HEAD, master) master@{7 weeks ago}: clone: from <repo URL>And even with patches displayed as part of the output:
$ git reflog show master --relative-date -p
3b6a7e870 (origin/master, origin/HEAD, master) master@{7 weeks ago}: clone: from <repo URL>
diff --git a/src/app/app.cpp b/src/app/app.cpp
index 7833fae15..6da32d596 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -33,7 +33,7 @@ static void SetCompleted(App *_app, bool &_) { App::s_isCompleted = _isCompleted; }
-App::Model *App::s_model = save::g_appFormatter.RegisterModel<App>(
+App::Model *App::s_model = save::ModelStorage::RegisterModel<App>(
APP_STRINGIFY(App),
save::AttributeAccess<&App::m_loadedSavefileVer,Common scenarios
Now that we have some understanding of what we’re looking at with the reference log output, let’s go over some scenarios. Example 1: Accidental use of `reset --hard`:
$ git reset --hard HEAD~3
# Oops.
$ git reflog
8a91155e0 (HEAD -> dev) HEAD@{0}: reset: moving to HEAD~3
ebe1ad46f (origin/dev) HEAD@{1}: commit: Build and Run on MacOS
[...]
# Now we use the commit before the reset (ebe1ad46f HEAD@{1})
$ git reset --hard ebe1ad46f # using "HEAD@{1}" in place of the hash is also acceptable
And we have effectively undone a hard reset. Note that in doing this, the first hard reset (the 8a91155e0 hash) is still present in the reference log and is now the new HEAD@{1}.
Example 2: Lost commit from a rebase
$ git rebase -i HEAD~5
Successfully rebased and updated refs/heads/dev.
# We need that last commit, however...
$ git reflog
0d9cae3c1 (HEAD -> dev) HEAD@{0}: rebase (finish): returning to refs/heads/dev
0d9cae3c1 (HEAD -> dev) HEAD@{1}: rebase (start): checkout HEAD~5
ebe1ad46f (origin/dev) HEAD@{2}: commit: Build and Run on MacOS
# Now we can cherry-pick the hash of the needed commit
$ git cherry-pick ebe1ad46f
# Don’t forget to commit!Example 3: Mistakenly deleted branch
$ git checkout master
M .gitignore
M .gitmodules
[...]
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
$ git branch -D dev
Deleted branch dev.
# Oops again.
$ git reflog
3853c23ba (HEAD -> master, origin/master) HEAD@{0}: checkout: moving from dev to master
ebe1ad46f (origin/dev) HEAD@{1}: commit: Build and Run on MacOS
[...]
# We can recreate the branch in whole from the last state we saw that branch in
$ git checkout -b dev ebe1ad46f
M .gitignore
M .gitmodules
[...]
Switched to a new branch 'dev'One Final Note
It is important to know that the reference log does not, by default, hold all history forever. Depending on the situation, entries will expire within 30 or 90 days. This behaviour can be changed via:
# You can also use "never" to keep everything or "now" to not keep a log (strongly discouraged)
# You can also make this affect all repos you use with --global
$ git config gc.reflogExpire 365.days
$ git config gc.reflogExpireUnreachable 90.days
Ultimately, the reference log serves well as a light in the dark, should things go wrong. These are some common examples, but most destructive operations short of deleting the .git folder can be recovered, from bad amends on commits to merges gone awry.
References and Additional Reading:
[table id=11 /]
[post_title] => Gitting Good – Leveraging the Reference Log For Disaster Recovery
[post_excerpt] =>
[post_status] => publish
[comment_status] => closed
[ping_status] => closed
[post_password] =>
[post_name] => gitting-good-leveraging-the-reference-log-for-disaster-recovery
[to_ping] =>
[pinged] =>
[post_modified] => 2026-08-27 20:10:16
[post_modified_gmt] => 2026-08-27 20:10:16
[post_content_filtered] =>
[post_parent] => 0
[guid] => https://keyvatech.com/?p=5294
[menu_order] => 0
[post_type] => post
[post_mime_type] =>
[comment_count] => 0
[filter] => raw
) [comment_count] => 0
[current_comment] => -1
[found_posts] => 164
[max_num_pages] => 21
[max_num_comment_pages] => 0
[is_single] =>
[is_preview] =>
[is_page] =>
[is_archive] =>
[is_date] =>
[is_year] =>
[is_month] =>
[is_day] =>
[is_time] =>
[is_author] =>
[is_category] =>
[is_tag] =>
[is_tax] =>
[is_search] =>
[is_feed] =>
[is_comment_feed] =>
[is_trackback] =>
[is_home] => 1
[is_privacy_policy] =>
[is_404] =>
[is_embed] =>
[is_paged] =>
[is_admin] =>
[is_attachment] =>
[is_singular] =>
[is_robots] =>
[is_favicon] =>
[is_posts_page] =>
[is_post_type_archive] =>
[query_vars_hash:WP_Query:private] => c0ac5b645bca5e8a351ed08806ccbf53
[query_vars_changed:WP_Query:private] =>
[thumbnails_cached] =>
[allow_query_attachment_by_filename:protected] =>
[stopwords:WP_Query:private] =>
[compat_fields:WP_Query:private] => Array
(
[0] => query_vars_hash
[1] => query_vars_changed
) [compat_methods:WP_Query:private] => Array
(
[0] => init_query_flags
[1] => parse_tax_query
) [query_cache_key:WP_Query:private] => wp_query:cb46a272b3f31498c88060117c9a6915
[tribe_is_event] =>
[tribe_is_multi_posttype] =>
[tribe_is_event_category] =>
[tribe_is_event_venue] =>
[tribe_is_event_organizer] =>
[tribe_is_event_query] =>
[tribe_is_past] =>
)