13

I want to get my entire session's undo history back when I load a Blender file. This would allow me to undo as far as would have been possible, had I not closed Blender.

Can any in-built feature or plugin support this?

safe_malloc
  • 231
  • 1
  • 5

1 Answers1

9

as you mentioned there is a limitation. this test shows you how many steps you can undo. In my pc I can undo only 32 steps:

import bpy

for i in range(0,100): bpy.ops.mesh.primitive_cube_add(enter_editmode=False, align='WORLD', location=(i, 0, 0)) bpy.ops.ed.undo_push(message="Add Cube")

for i in range(0,100): bpy.ops.ed.undo()

I decompiled undo script

void ED_undo_push(bContext *C, const char *str)
{
  CLOG_INFO(&LOG, 1, "name='%s'", str);
  WM_file_tag_modified();

wmWindowManager *wm = CTX_wm_manager(C); int steps = U.undosteps;

/* Ensure steps that have been initialized are always pushed,

  • even when undo steps are zero.
  • Note that some modes (paint, sculpt) initialize an undo step before an action runs,
  • then accumulate changes there, or restore data from it in the case of 2D painting.
  • For this reason we need to handle the undo step even when undo steps is set to zero.

*/ if ((steps <= 0) && wm->undo_stack->step_init != NULL) { steps = 1; } if (steps <= 0) { return; }

/* Only apply limit if this is the last undo step. */ if (wm->undo_stack->step_active && (wm->undo_stack->step_active->next == NULL)) { BKE_undosys_stack_limit_steps_and_memory(wm->undo_stack, steps - 1, 0); }

BKE_undosys_step_push(wm->undo_stack, C, str);

if (U.undomemory != 0) { const size_t memory_limit = (size_t)U.undomemory * 1024 * 1024; BKE_undosys_stack_limit_steps_and_memory(wm->undo_stack, -1, memory_limit); }

if (CLOG_CHECK(&LOG, 1)) { BKE_undosys_print(wm->undo_stack); } }

you can see how limitation calculated:

const size_t memory_limit = (size_t)U.undomemory * 1024 * 1024;

as @Jachym Michal said in comments increasing this limitation is not recommended

I suggest you use Git.you can easily undo (checkout) by git commands

Git is a distributed version control system for tracking changes in source code during software development.

but if you like the automatic way you can commit after each change:

import subprocess
from subprocess import Popen, PIPE, STDOUT

import re

undo step

step = 0

repository path

repoDir = 'C:/Users/Kamali/Desktop/Test'

def gitCommit(message): global step step += 1 cmd = 'git add . && git commit -am '+message p = subprocess.Popen(cmd, cwd=repoDir) p.wait()

def undo(): global step step -= 1 checkoutTo(step)

check out to specific step

def checkoutTo(Index): cmd = 'git checkout ' + getStepRevision() p = subprocess.Popen(cmd, cwd=repoDir) p.wait()

get specific revision from git log history 546bece2c66acb454878500689e3017b92c43d28

def getStepRevision(): cmd = 'powershell.exe git show HEAD~'+str(step)+' --pretty=oneline --no-patch' p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True, cwd=repoDir) output = p.stdout.read() p.wait()

revision = re.search('(.*?)\s',output.decode('utf-8')).group(1)
print(revision)


Update

if you don't know git commands you can use git extension

after you undo (check out) you have to close and reopen blender to refresh but these lines of code help you to reload it easy

import bpy

bpy.ops.wm.open_mainfile(filepath=bpy.data.filepath)

Tutorial

run this code and see what happened!!!

import subprocess
from subprocess import Popen, PIPE, STDOUT

import bpy

repoDir = 'C:/Users/Kamali/Desktop/New folder'

for i in range(0,100): bpy.ops.mesh.primitive_cube_add(enter_editmode=False, align='WORLD', location=(i, 0, 0)) bpy.ops.ed.undo_push(message="Add Cube")

def gitCommit(message): bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath) add = 'git add .' p = subprocess.Popen(add, cwd=repoDir) p.wait() commit = 'git commit -m ' + message p = subprocess.Popen(commit, cwd=repoDir) p.wait()

I could commit 100 steps with code

enter image description here