Wednesday, July 27, 2016

Sending WM_COPYDATA in Python with ctypes

Putting together pieces from a few online sources, here is example code that:
  • Supports 32-bit and 64-bit Python
  • Uses only standard libraries (for Python 2.5+)
  • Can be used to send commands to the SciTE code editor

In particular, there is no win32gui dependency.

import sys
import ctypes
from ctypes import wintypes

WM_COPYDATA = 0x4a

context64bit = sys.maxsize > 2**32
if context64bit:
  class COPYDATASTRUCT(ctypes.Structure):
    _fields_ = [('dwData', ctypes.c_ulonglong),
      ('cbData', ctypes.wintypes.DWORD),
      ('lpData', ctypes.c_void_p)]
else:
  class COPYDATASTRUCT(ctypes.Structure):
    _fields_ = [('dwData', ctypes.wintypes.DWORD),
      ('cbData', ctypes.wintypes.DWORD),
      ('lpData', ctypes.c_void_p)]

def findWindow(windowClass):
  receiver = None
  hwnd = ctypes.windll.user32.FindWindowA(
    windowClass, receiver)
  return hwnd or None

def sendMessage(message, hwnd, dwData=0):
  assert isinstance(message, str)
  
  sender_hwnd = 0
  buf = ctypes.create_string_buffer(message)
  copydata = COPYDATASTRUCT()
  copydata.dwData = dwData
  copydata.cbData = buf._length_
  copydata.lpData = ctypes.cast(buf, ctypes.c_void_p)
  return ctypes.windll.user32.SendMessageA(
    hwnd, WM_COPYDATA, sender_hwnd,
    ctypes.byref(copydata))




In Python 3, the parameter would presumably be a bytes object and not a str object.

Monday, July 11, 2016

Moving a directory in git from one repo to another

I recently moved a directory from one repo to another, and preserved both commit history and last-modified times.

Summarizing what I've learned from a few different sources. These commands should be run in Linux; there is likely a PowerShell equivalent but it was not forthcoming. (Speaking of sharing a repo across platforms, adding a .gitattributes with * text=auto to handle newline characters has worked well for me).

Let's say we want to move "dir/directory_src" of "repo_one" to "dir/directory_dest" of "repo_two".

First let's move all files from directory_src to directory_dest and remove all files outside of directory_dest.

mkdir ~/repos
cd ~/repos
git clone https://servername/repo_one
cd repo_one
# intentionally prevent ourselves from pushing changes
git remote rm origin
# run the following and look at the resulting filenames to see if they look correct
git ls-files -s | sed "s-\tdir/directory_src/-\tdir/directory_dest/-"
# rewrite history so that "dir/directory_src" moves to "dir/directory_dest"
git filter-branch --index-filter  'git ls-files -s | sed "s-\tdir/directory_src/-\tdir/directory_dest/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
updated:
git filter-branch --index-filter  'git ls-files -s | sed "s-\tdir/directory_src/-\tdir/directory_dest/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && if [ -f "$GIT_INDEX_FILE.new" ]; then mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"; fi' HEAD
(move or delete the directory .git/refs/original)
cd ~/repos/repo_one

# run the following and look that files in dir/directory_dest aren't included
git ls-files | egrep -v ^dir/directory_dest/
# rewrite history and remove all other files
git filter-branch --tree-filter 'rm -rf $(git ls-files | egrep -v ^dir/directory_dest/)' -- --all
# delete empty commits (optional)
git filter-branch --commit-filter 'git_commit_non_empty_tree "$@"' HEAD
Now let's copy the files into repo_two.
cd ~/repos
git clone https://servername/repo_two
cd repo_two
git remote add from_repo_one ~/repos/repo_one
git pull from_repo_one master
git remote rm from_repo_one
# after confirming that everything looks right,
# run git push.
"repo_two" should now contain "dir/directory_dest" and all of its contents.

Note: the tree filter removal script will leave filenames that contain spaces or tabs.

Sources:

Moving files from one Git repository to another


How can I move a directory in a Git repo