git rebase - pop each commit into working dir to easily edit, re-use commit msg - git-rebase

The git-rebase edit command seems to have the right idea, but we have to run git undo; git reset; to make changes available to edit....
Is there a good way to be able to cruise through an entire set of commits, pop each one into the working dir, one after the next?
Before I submit my PRs, I like to go through my commits. Instead of just looking at a read-only diff, I want to live edit each commit, as if I had just written it.
I also want the commit message to be pre-populated, don't want to go hunting for the correct commit message each time.

As you mentioned, the edit cmd inside git-rebase doesn't quite do all the steps you need to do to actually edit (mutate) a commit.
First, you may decide, you don't want to look at merge conflicts.
git rebase --strategy recursive --strategy-option theirs -i <base-sha>
Since you are going to check/edit each commit... if you aren't changing too many things, you should be able to spot what is wrong about a commit, based on a previous change you did. Notably, if you added a code comment in an early commit, and saw a subsequent commit deletes that comment, you should simply restore the comment. This is easy in vscode's side by side diff view (which I use all the time).
Finally, we do need to use some sort of rebase exec command to pop changes into our working directory:
exec MSG=$(git log -1 --format=%B HEAD); git undo; git reset; echo "$MSG" > $GIT_DIR/LAST_COMMIT_MSG; echo "editing commit:\n\n$MSG\n";
Maybe, you use vscode, we can also open the edited files for you:
code $(git diff --staged --name-only)
UPDATE: This doesn't open the actual diffs, so is a waste for me, not included in final command. Maybe a vscode shortcut key would work, or if this entire review flow was simply packaged up into a vscode extension.
This exec command will always fail, so we'll want --no-reschedule-failed-exec
// Putting it all together ... there are further changes below.
GIT_SEQUENCE_EDITOR=: git rebase --exec 'MSG=$(git log -1 --format=%B HEAD); git undo; git restore --staged $(git diff --name-only --staged --diff-filter=r); echo "$MSG" > $GIT_DIR/LAST_COMMIT_MSG; echo "editing commit:\n\n$MSG\n";' --strategy recursive --no-reschedule-failed-exec --strategy-option theirs -i 315abbd5b
To cycle onto the next commit, simply run:
git add --all && git commit && git rebase --continue
We'll then need this prepare-commit-msg script to re-use the LAST_COMMIT_MSG file:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
COMMIT_MSG_FILE=$1
# COMMIT_SOURCE=$2
# SHA1=$3
if [ -f $GIT_DIR/LAST_COMMIT_MSG ]; then
cat $GIT_DIR/LAST_COMMIT_MSG $COMMIT_MSG_FILE > temp_commit_msg && mv temp_commit_msg $COMMIT_MSG_FILE
rm $GIT_DIR/LAST_COMMIT_MSG
fi
Add this few hooks to wipe any stale LAST_COMMIT_MSG:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo "some git-hook: wiping $GIT_DIR/LAST_COMMIT_MSG: $(cat $GIT_DIR/LAST_COMMIT_MSG)"
rm $GIT_DIR/LAST_COMMIT_MSG
This way, you just run git commit when you are done editing a commit, and, you'll get to re-use/tweak the original commit message.
If using husky, you'll need to:
echo ".husky/prepare-commit-msg" >> $(git rev-parse --show-toplevel)/../main-worktree/.git/info/exclude
echo ".husky/pre-rebase" >> $(git rev-parse --show-toplevel)/../main-worktree/.git/info/exclude
echo ".husky/post-rewrite" >> $(git rev-parse --show-toplevel)/../main-worktree/.git/info/exclude
echo ".husky/post-commit" >> $(git rev-parse --show-toplevel)/../main-worktree/.git/info/exclude
# paste it in:
code -n .husky/prepare-commit-msg .husky/pre-rebase .husky/post-rewrite .husky/post-commit
chmod +x .husky/prepare-commit-msg
chmod +x .husky/pre-rebase
chmod +x .husky/post-rewrite
chmod +x .husky/post-commit
UPDATE:
This whole command becomes quite a bear, so let's use an alias to clean it up:
git config --global --edit
Add few aliases:
next = "!sh -c 'git add --all && git commit $# && git rebase --continue' -"
redo = !echo "$(git log -1 --format=%B HEAD)" > $GIT_DIR/LAST_COMMIT_MSG && git undo && git restore --staged $(git diff --name-only --staged --diff-filter=ard) > /dev/null 2>&1 || true && cat $GIT_DIR/LAST_COMMIT_MSG && echo '' && git -c advice.addEmptyPathspec=false add -N $(git ls-files --others --exclude-standard) > /dev/null 2>&1 || true
review-stack = "!GIT_SEQUENCE_EDITOR=: git rebase --exec 'git redo' --strategy recursive --no-reschedule-failed-exec --strategy-option theirs --interactive"
Finally, using alias:
git review-stack 315abbd5b
# Go to next commit, no frills:
git next --no-edit --no-verify
# If you made changes:
git next

Prep:
Add these git alias's:
git config --global --edit
Paste in
[alias]
next = "!sh -c 'git add --all && git commit $# && git rebase --continue' -"
redo = !echo "$(git log -1 --format=%B HEAD)" > $GIT_DIR/LAST_COMMIT_MSG && git undo && git restore --staged $(git diff --name-only --staged --diff-filter=ard) > /dev/null 2>&1 || true && cat $GIT_DIR/LAST_COMMIT_MSG || true && echo '' && git -c advice.addEmptyPathspec=false add -N $(git ls-files --others --exclude-standard) > /dev/null 2>&1 || true
redo is a beast, I am NOT good at bash, basically cobbled this together using google + SO
To pre-populate COMMIT_EDITMSG with your last commit message, setup these git hooks. Instructions here are just for husky for now, if you aren't using husky, it's even easier, just put hooks into .git/hooks dir.
# using worktrees:
LOCAL_GITIGNORE=$(git rev-parse --show-toplevel)/../main-worktree/.git/info/exclude
# not using worktrees:
LOCAL_GITIGNORE=$(git rev-parse --show-toplevel)/.git/info/exclude
echo ".husky/prepare-commit-msg" >> $LOCAL_GITIGNORE
echo ".husky/pre-rebase" >> $LOCAL_GITIGNORE
echo ".husky/post-rewrite" >> $LOCAL_GITIGNORE
echo ".husky/post-commit" >> $LOCAL_GITIGNORE
chmod +x .husky/prepare-commit-msg
chmod +x .husky/pre-rebase
chmod +x .husky/post-rewrite
chmod +x .husky/post-commit
code .husky/prepare-commit-msg .husky/pre-rebase .husky/post-rewrite .husky/post-commit
.husky/prepare-commit-msg is unique:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
COMMIT_MSG_FILE=$1
# COMMIT_SOURCE=$2
# SHA1=$3
if [ -f $GIT_DIR/LAST_COMMIT_MSG ]; then
# vscode commits happen to run with this command:
# git -c user.useConfigOnly=true commit --quiet --allow-empty-message --file - [3774ms]
# So, we won't want to use the last commit message in that case.
# LAST_COMMIT_MSG will be cleaned up post-commit, if commit succeeds
#
# TODO:
# if commit msg from vscode is empty (first line of $COMMIT_MSG_FILE is empty),
# And second line starts with a "#"
# then actually fill in the missing commit message!
# Maybe we can read `COMMIT_SOURCE=$2`
# instead of reading the `$COMMIT_MSG_FILE`
# https://www.google.com/search?q=bash+check+if+first+line+of+file+is+empty
if [ "$(git config user.useConfigOnly)" != "true" ]; then
cat $GIT_DIR/LAST_COMMIT_MSG $COMMIT_MSG_FILE > temp_commit_msg && mv temp_commit_msg $COMMIT_MSG_FILE
# It's been used once, get rid of it?
# rm $GIT_DIR/LAST_COMMIT_MSG;
# This is cleaned up in post-commit hook.
# So you can abort commit, edit more, re-commit, and still retain this commit message.
fi
fi
The remaining 3 are all the same:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
if [ -f $GIT_DIR/LAST_COMMIT_MSG ]; then
# echo "some git hook: wiping $GIT_DIR/LAST_COMMIT_MSG: $(cat $GIT_DIR/LAST_COMMIT_MSG)"
rm $GIT_DIR/LAST_COMMIT_MSG
fi
Launch it!
git rebase --exec 'git redo' -i 315abbd5b
next commit
git next
To focus in on a certain variable name throughout a PR, you could run:
git rebase --exec 'ag -0 -l newVarNameOfInterest app/greenfield/project && git redo || echo "ok"' -i 315abbd5b
to install ag run brew install the_silver_searcher

Related

git-rebase: Make bad variable name in large PR stack disappear

I had a JSX component named AccountNode, and accidentally created a TS interface also named AccountNode, but the two are distinct. One is for a row in a table (ui), and the other is for a piece of data in a tree (a node).
How can I make the "AccountNode" name not appear anywhere in history?
Similarly, as mentioned in the comments, you may have a bad string constant bring passed around, a so-called "magic string". It may be hard to rewrite code to not use this magic string, but this git rebase recipe will help.
Example, I don't want to hard-code the string "load_more" so am going to redo/tweak those commits:
git rebase --exec 'git difflastcommitadded | ag load_more && { echo "found load_more"; git redo; } || echo "ok"' --no-reschedule-failed-exec -i 315abbd5b
Setup:
Install ag (brew install the_silver_searcher) (grep/ack alternative)
Add this git difflastcommitadded alias:
git config --global --edit
brew install the_silver_searcher
Paste:
[alias]
# From: https://stackoverflow.com/questions/73981158/git-rebase-make-bad-name-in-large-pr-stack-disappear/73981159#73981159
difflastcommitadded = !git diff HEAD^..HEAD --no-ext-diff --unified=0 --exit-code -a --no-prefix | egrep '^\\+'
Alternatively, if you want to completely abolish a variable name from a folder in your repo, continue reading this less flexible example:
git rebase --exec 'ag -0 -l AccountNode ui/app && git redo || echo "ok"' -i 315abbd5b
This will rebase, but stop whenever there's a commit that introduced AccountNode inside the ui/app folder, undo that commit, and pop those changes into your working directory, so you can fix that in your ide.
Once you fix an instance of AccountNode you can proceed with nice safety checks using this script:
ag -0 -l AccountNode ui/app/pages/AccountManagement2 && echo "still contains AccountNode" || {
yarn --cwd ui run jest -i && git add . && git commit && git rebase --continue
}
In my instance, git commit runs prettier+eslint, but not tests (those run in ci right now)
This depends on the git redo alias mentioned here: git rebase - pop each commit into working dir to easily edit, re-use commit msg

Is git ls-files -om and git ls-files -o -m same

I have a doubt in git command that.
-m, --modified
Show modified files in the output
-o, --others
Show other (i.e. untracked) files in the output
I have wrote code for git is bellow
git ls-files -om
So i want to know that "-o -m" and "-om" are same or "-om" is wrong. I got error in one place

Fully reset a corrupted git submodule in SourceTree

First off, I am not using command line git at all. I am only using SourceTree's gui interface. I would prefer to solve my problem this way; if possible.
Somehow, my submodule has become corrupted. Attempting to fetch or pull gives me the following error:
I haven't found any answers for this particular problem. I am fortunate in that my remote master is ok, 100% up to date, and I have no local changes. So, I think the easiest way will be to just fully reset my local submodule.
However, I can't figure out how to do so.
I considered trying to remove my submodule and then re add it. However, I have had problems with that in the past, and so am gun shy.
I found handfuls of posts about resetting to a specific commit. However, the SourceTree gui is failing to populate my history because of this error.
Any help would be appreciated
Thank you
You can try this
Right click at your conflicted branch at BRANCHES and delete it.
Double click at your remote branch at REMOTES to re-download and switch to that branch.
Easy, good luck.
I finally did get this fixed. I had to give up and use the command line.
I found this page (git fatal: failed to read object xxx: Invalid argument). That pointed me to using the "git fsck --full" command.
That pointed me to a very specific folder in the .git hierarchy that was corrupted.
I needed to delete this folder, but doing so wasn't easy. Windows would let me delete it. Not in safe mode. Not with cmd del or rmdir. I had to run a scan disk from windows on my entire drive. That ended up detecting the folder and removing it.
Finally, from there, I was able to fetch and pull master again.
From all the solutions I was able to find to perform an actually hard reset with proper cleanup and reinitialization of submodules nothing worked. So I ended up creating a custom script. Following will perform a full cleanup of your submodules and will recreate them as if you were cloning the parent repo.
#!/bin/bash
echo "Backing up current .gitmodules"
cp -f .gitmodules .gitmodules.bkp
has_submodules=$(echo "$(git submodule)")
if [ "$has_submodules" != "" ]; then
git submodule deinit --force .
rm -rf .git/modules
git submodule | cut -c43- | while read -r line; do (git rm -f "$line"); done
fi
cp -f .gitmodules.bkp .gitmodules
PARAMS=("path" "url" "branch")
CHUNKS=${#PARAMS[#]}
PARAMS_STR=$(IFS='|'; echo "${PARAMS[*]}")
readarray -t SUBMODULES_INFO_ARRAY < <(git config -f .gitmodules --get-regexp '^submodule\..*\.('"$PARAMS_STR"')$')
SUBMODULES_INFO_ARRAY_STR=$(IFS='|'; echo "${SUBMODULES_INFO_ARRAY[*]}")
function process_submodules_parsed_array()
{
name=${SUBMODULES_PARSED_ARRAY[0]}
path=${SUBMODULES_PARSED_ARRAY[1]}
repo_url=${SUBMODULES_PARSED_ARRAY[2]}
branch=${SUBMODULES_PARSED_ARRAY[3]}
echo "Running: git submodule add --force -b "$branch" --depth 1 --name "$name" -- "$repo_url" "$path""
git submodule add --force -b "$branch" --depth 1 --name "$name" -- "$repo_url" "$path"
}
echo "Parsing current .gitmodules"
for((chunk=0; chunk<${#SUBMODULES_INFO_ARRAY[#]}; chunk+=CHUNKS))
do
section_name="$(sed -nE 's/^submodule\.(.*?)\.'${PARAMS[0]}'\s(.*?)$/\1/p' <<< "${SUBMODULES_INFO_ARRAY[chunk]}")"
SUBMODULES_PARSED_ARRAY=("$section_name")
for ((param_i=0; param_i<CHUNKS; param_i+=1))
do
param_unparsed="${SUBMODULES_INFO_ARRAY[chunk+param_i]}"
param_parsed="$(sed -nE 's/^submodule.*?\.'${PARAMS[param_i]}'\s(.*?)$/\1/p' <<< "$param_unparsed")"
SUBMODULES_PARSED_ARRAY+=($param_parsed)
done
process_submodules_parsed_array
done
echo "Restoring .gitmodules"
mv -f .gitmodules.bkp .gitmodules
echo "Initializing submodules"
git submodule init
git submodule update

gitlab ci does not update code at remote server

I want to deploy ma test app from local repo to gitlab repo and with gitlab ci push it to my remote server. SSH connection is working, gitlab CI shows that job is passed, but code on remote server is not updated.
I made bare repo in: /home/repos/testDeploy.git
And folder for files is in: /home/example.com/web/testDeploy
I added
My .gitlab-ci.yml file
stages:
- deploy
deployment:
stage: deploy
environment:
name: production
url: http://www.example.com/testDeploy
only:
- master
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- chmod 600 ~/.ssh/id_rsa_gitlab && chmod 700 ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
script:
- git remote add production ssh://user#server:port/home/repos/testDeploy.git
- git push -f production master
- echo "Deployed to production!"
Also, i have post-receive hook:
#!/bin/sh
git --git-dir=/home/repos/testDeploy.git --work-tree=/home/example.com/web/testDeploy checkout -f
I make changes in my local repo, commit and push to origin master to gitlab. Job is passed, but as I mention above, file on remote server is not update.
Output from gitlab job is:
Fetching changes...
HEAD is now at 595db67 as
Checking out 595db67b as master...
Skipping Git submodules setup
$ which ssh-agent || ( apt-get update -y && apt-get install openssh- client -y )
/usr/bin/ssh-agent
$ eval $(ssh-agent -s)
Agent pid 40589
$ chmod 600 ~/.ssh/id_rsa_gitlab && chmod 700 ~/.ssh
$ [[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
$ git branch
* (HEAD detached at 595db67)
master
production
$ git push -f production master
Everything up-to-date
$ echo "Deployed to production!"
Deployed to production!
Job succeeded
What I am doing wrong? Can you someone help me please to figure it out? Thank you for all your answers.

Pushing on a Git repository with Travis-CI

I want to execute a script every time I push into the master, this script will create some files that I want to commit and push. The log of the Travis build seems to be adding the files, committing and pushing, but nothing happens.
My .travis.yml is this one:
before_install:
- openssl aes-256-cbc -K $encrypted_290c3b2c061d_key -iv $encrypted_290c3b2c061d_iv -in id_rsa.enc -out /tmp/id_rsa -d
- eval "$(ssh-agent -s)" # Start the ssh agent
- chmod 600 /tmp/id_rsa
- ssh-add /tmp/id_rsa
install:
- wget --user $docencia_ac_username --password $docencia_ac_password http://docencia.ac.upc.edu/FIB/grau/PEC/Protegido/Documentacion/eines-sisa-64BITS.tgz
- tar -xf eines-sisa-64BITS.tgz
- export PATH=$PATH:$(pwd)/eines-sisa/bin
script:
- chmod +x ./compile_to_hex.sh
- "./compile_to_hex.sh"
after_success:
- rm -rf $TRAVIS_BUILD_DIR/tmp/
- git config --local user.name "Marc43"
- git config --local user.email "my mail"
- git add $TRAVIS_BUILD_DIR/hex/*
- git commit -m "At $(date) hex files builded - travis-ci [skip ci]"
- git push git#github.com:Marc43/sisa_hexbuilder.git master > /dev/null 2>&1
before_deploy:
- rm -rf eines-sisa*
in the git user.email it really goes my email but I decided to delete it for the question.
And the travis log for the build is:
$ git add $TRAVIS_BUILD_DIR/hex/*
$ git commit -m "At $(date) hex files builded - travis-ci [skip ci]"
[detached HEAD 10e7e48] At Sun Apr 15 08:06:17 UTC 2018 hex files builded - travis-ci [skip ci]
4 files changed, 21 insertions(+)
create mode 100644 hex/exemple.hex
create mode 100644 hex/joc_io.hex
create mode 100644 hex/tb_branch.hex
create mode 100644 hex/tb_sum.hex
I know that there is another way to do this via GitHub tokens or something like that, anyway I don't know how to do it one way or the other. I've tried to do it with the deploy too but it never uploaded my files, just tagged the same commit I did. Any ideas?
Thank you,
Marc
I run several repositories that need documentation compiling to distributable formats (e.g. AsciiDoc to HTML, MD to PDF), rather than having to build and commit every time I want to update the distributable, I’d like to automate this process. This is where I use TravisCI as a build server.
before_install:
- sudo apt-get install pandoc
- gem install asciidoctor
script:
- make
after_success:
- .travis/push.sh
env:
global:
secure: hZJlqgOzA2zIUJSWIka0PylqNaTkfHq+kS48RrHmocrK0vLyCW7ECWrzez2f2RVdTNzPi0b+yJq2uCbFfWjImZqg+XY1I75/CVVdSYMk7PJkYZ/iBDixMYY8CAkRRd5yZft9uZAdZzR4KLCPN18n7qfISv/M9VA8989NKcVyiEU=
push.sh
#!/bin/sh
setup_git() {
git config --global user.email "travis#travis-ci.org"
git config --global user.name "Travis CI"
}
commit_website_files() {
git checkout -b gh-pages
git add . *.html
git commit --message "Travis build: $TRAVIS_BUILD_NUMBER"
}
upload_files() {
git remote add origin-pages https://${GH_TOKEN}#github.com/MVSE-outreach/resources.git > /dev/null 2>&1
git push --quiet --set-upstream origin-pages gh-pages
}
setup_git
commit_website_files
upload_files
Reference - https://gist.github.com/willprice/e07efd73fb7f13f917ea