List most recent mercurial hashes - bash

Does hg have a nice way to list just the hashes of the most recent commits in the repo on separate lines? hg id -i only gives me the current commit.
Something equivalent to the -l parameter in hg log such that I could do something like:
$ hg id -i -l 3
1eb0c5a3002e <--- youngest commit
321c6693361e <--- 2nd youngest commit
647724838c03 <--- 3rd youngest commit
To give some context, I have a bash script which I pass a mercurial hash to and it builds a docker image,
./build_image.sh 647724838c03
I am trying to get command line completion for the hash argument so I need a command to generate all the hashes which I can drop into compgen:
local HASHES=$(cd $REPO && hg id -i -l 10)
COMPREPLY=( $(compgen -W "$HASHES" -- $cur) )
Most of the time the hash I'm wanting to build will be the checked out commit so hg id -i will work okay, but getting a full list of the last 10 hashes would be nice!
Thanks in advance for any help!
EDIT
I got a work around using grep and awk:
$ hg log -l 5 | grep changeset | awk -vn=12 '{print substr($0,length($0)-n+1)}'
1eb0c5a3002e
ec0db890a6c3
321c6693361e
4a1e2ca2df06
2d1ef7d57485
This isn't great though:
makes assumptions about how hg log outputs (which might change in different versions)
that awk command is a bit too mystical for me :)
doesn't work for Windows (assuming you're trying to solve a different problem to bash tab completion)

That's what you have log for. Template the output to your needs, e.g.
hg log -l10 --template="{node|short} {date} {author}\n"
gives you output like
0c4520bbf388 2017-06-07 12:25 +0200 username <user#example.com>
Checkout hg help template for more templating options. You can basically apply templating to all relevant commands which generate these kind of outputs.
Mind: if you do not sort log explicitly by commit date, it will return you the 10 newest commits in your repository - which does not necessarily reflect order of commit time. You can do that by giving additionally a revset and sorting by it:
hg log --rev="sort(date('<now'))" --template="{node}\n" -l5

Related

Git: Can't push after large committed file was part of previous commit [duplicate]

I accidentally dropped a DVD-rip into a website project, then carelessly git commit -a -m ..., and, zap, the repo was bloated by 2.2 gigs. Next time I made some edits, deleted the video file, and committed everything, but the compressed file is still there in the repository, in history.
I know I can start branches from those commits and rebase one branch onto another. But what should I do to merge the 2 commits so that the big file doesn't show in the history and is cleaned in the garbage collection procedure?
Use the BFG Repo-Cleaner, a simpler, faster alternative to git-filter-branch specifically designed for removing unwanted files from Git history.
Carefully follow the usage instructions, the core part is just this:
$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
Any files over 100MB in size (that aren't in your latest commit) will be removed from your Git repository's history. You can then use git gc to clean away the dead data:
$ git reflog expire --expire=now --all && git gc --prune=now --aggressive
After pruning, we can force push to the remote repo*
$ git push --force
*NOTE: cannot force push a protect branch on GitHub
The BFG is typically at least 10-50x faster than running git-filter-branch, and generally easier to use.
Full disclosure: I'm the author of the BFG Repo-Cleaner.
What you want to do is highly disruptive if you have published history to other developers. See “Recovering From Upstream Rebase” in the git rebase documentation for the necessary steps after repairing your history.
You have at least two options: git filter-branch and an interactive rebase, both explained below.
Using git filter-branch
I had a similar problem with bulky binary test data from a Subversion import and wrote about removing data from a git repository.
Say your git history is:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
Note that git lola is a non-standard but highly useful alias. (See the addendum at the end of this answer for details.) The --name-status switch to git log shows tree modifications associated with each commit.
In the “Careless” commit (whose SHA1 object name is ce36c98) the file oops.iso is the DVD-rip added by accident and removed in the next commit, cb14efd. Using the technique described in the aforementioned blog post, the command to execute is:
git filter-branch --prune-empty -d /dev/shm/scratch \
--index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
--tag-name-filter cat -- --all
Options:
--prune-empty removes commits that become empty (i.e., do not change the tree) as a result of the filter operation. In the typical case, this option produces a cleaner history.
-d names a temporary directory that does not yet exist to use for building the filtered history. If you are running on a modern Linux distribution, specifying a tree in /dev/shm will result in faster execution.
--index-filter is the main event and runs against the index at each step in the history. You want to remove oops.iso wherever it is found, but it isn’t present in all commits. The command git rm --cached -f --ignore-unmatch oops.iso deletes the DVD-rip when it is present and does not fail otherwise.
--tag-name-filter describes how to rewrite tag names. A filter of cat is the identity operation. Your repository, like the sample above, may not have any tags, but I included this option for full generality.
-- specifies the end of options to git filter-branch
--all following -- is shorthand for all refs. Your repository, like the sample above, may have only one ref (master), but I included this option for full generality.
After some churning, the history is now:
$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A login.html
| * cb14efd Remove DVD-rip
| | D oops.iso
| * ce36c98 Careless
|/ A oops.iso
| A other.html
|
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
Notice that the new “Careless” commit adds only other.html and that the “Remove DVD-rip” commit is no longer on the master branch. The branch labeled refs/original/refs/heads/master contains your original commits in case you made a mistake. To remove it, follow the steps in “Checklist for Shrinking a Repository.”
$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now
For a simpler alternative, clone the repository to discard the unwanted bits.
$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo
Using a file:///... clone URL copies objects rather than creating hardlinks only.
Now your history is:
$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
The SHA1 object names for the first two commits (“Index” and “Admin page”) stayed the same because the filter operation did not modify those commits. “Careless” lost oops.iso and “Login page” got a new parent, so their SHA1s did change.
Interactive rebase
With a history of:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
you want to remove oops.iso from “Careless” as though you never added it, and then “Remove DVD-rip” is useless to you. Thus, our plan going into an interactive rebase is to keep “Admin page,” edit “Careless,” and discard “Remove DVD-rip.”
Running $ git rebase -i 5af4522 starts an editor with the following contents.
pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
Executing our plan, we modify it to
edit ce36c98 Careless
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
# ...
That is, we delete the line with “Remove DVD-rip” and change the operation on “Careless” to be edit rather than pick.
Save-quitting the editor drops us at a command prompt with the following message.
Stopped at ce36c98... Careless
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
As the message tells us, we are on the “Careless” commit we want to edit, so we run two commands.
$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue
The first removes the offending file from the index. The second modifies or amends “Careless” to be the updated index and -C HEAD instructs git to reuse the old commit message. Finally, git rebase --continue goes ahead with the rest of the rebase operation.
This gives a history of:
$ git lola --name-status
* 93174be (HEAD, master) Login page
| A login.html
* a570198 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
which is what you want.
Addendum: Enable git lola via ~/.gitconfig
Quoting Conrad Parker:
The best tip I learned at Scott Chacon’s talk at linux.conf.au 2010, Git Wrangling - Advanced Tips and Tricks was this alias:
lol = log --graph --decorate --pretty=oneline --abbrev-commit
This provides a really nice graph of your tree, showing the branch structure of merges etc. Of course there are really nice GUI tools for showing such graphs, but the advantage of git lol is that it works on a console or over ssh, so it is useful for remote development, or native development on an embedded board …
So, just copy the following into ~/.gitconfig for your full color git lola action:
[alias]
lol = log --graph --decorate --pretty=oneline --abbrev-commit
lola = log --graph --decorate --pretty=oneline --abbrev-commit --all
[color]
branch = auto
diff = auto
interactive = auto
status = auto
Why not use this simple but powerful command?
git filter-branch --tree-filter 'rm -f DVD-rip' HEAD
The --tree-filter option runs the specified command after each checkout of the project and then recommits the results. In this case, you remove a file called DVD-rip from every snapshot, whether it exists or not.
If you know which commit introduced the huge file (say 35dsa2), you can replace HEAD with 35dsa2..HEAD to avoid rewriting too much history, thus avoiding diverging commits if you haven't pushed yet. This comment courtesy of #alpha_989 seems too important to leave out here.
See this link.
(The best answer I've seen to this problem is: https://stackoverflow.com/a/42544963/714112 , copied here since this thread appears high in Google search rankings but that other one doesn't)
🚀 A blazingly fast shell one-liner 🚀
This shell script displays all blob objects in the repository, sorted from smallest to largest.
For my sample repo, it ran about 100 times faster than the other ones found here.
On my trusty Athlon II X4 system, it handles the Linux Kernel repository with its 5,622,155 objects in just over a minute.
The Base Script
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
When you run above code, you will get nice human-readable output like this:
...
0d99bb931299 530KiB path/to/some-image.jpg
2ba44098e28f 12MiB path/to/hires-image.png
bd1741ddce0d 63MiB path/to/some-video-1080p.mp4
🚀 Fast File Removal 🚀
Suppose you then want to remove the files a and b from every commit reachable from HEAD, you can use this command:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
After trying virtually every answer in SO, I finally found this gem that quickly removed and deleted the large files in my repository and allowed me to sync again: http://www.zyxware.com/articles/4027/how-to-delete-files-permanently-from-your-local-and-remote-git-repositories
CD to your local working folder and run the following command:
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all
replace FOLDERNAME with the file or folder you wish to remove from the given git repository.
Once this is done run the following commands to clean up the local repository:
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
Now push all the changes to the remote repository:
git push --all --force
This will clean up the remote repository.
100 times faster than git filter-branch and simpler
There are very good answers in this thread, but meanwhile many of them are outdated. Using git-filter-branch is no longer recommended, because it is difficult to use and awfully slow on big repositories.
git-filter-repo is much faster and simpler to use.
git-filter-repo is a Python script, available at github: https://github.com/newren/git-filter-repo . When installed it looks like a regular git command and can be called by git filter-repo.
You need only one file: the Python3 script git-filter-repo. Copy it to a path that is included in the PATH variable. On Windows you may have to change the first line of the script (refer INSTALL.md). You need Python3 installed installed on your system, but this is not a big deal.
First you can run
git filter-repo --analyze
This helps you to determine what to do next.
You can delete your DVD-rip file everywhere:
git filter-repo --invert-paths --path-match DVD-rip
Filter-repo is really fast. A task that took around 9 hours on my computer by filter-branch, was completed in 4 minutes by filter-repo. You can do many more nice things with filter-repo. Refer to the documentation for that.
Warning: Do this on a copy of your repository. Many actions of filter-repo cannot be undone. filter-repo will change the commit hashes of all modified commits (of course) and all their descendants down to the last commits!
These commands worked in my case:
git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
It is little different from the above versions.
For those who need to push this to github/bitbucket (I only tested this with bitbucket):
# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local
git push --all --prune --force
# Once you pushed, all your teammates need to clone repository again
# git pull will not work
According to GitHub Documentation, just follow these steps:
Get rid of the large file
Option 1: You don't want to keep the large file:
rm path/to/your/large/file # delete the large file
Option 2: You want to keep the large file into an untracked directory
mkdir large_files # create directory large_files
touch .gitignore # create .gitignore file if needed
'/large_files/' >> .gitignore # untrack directory large_files
mv path/to/your/large/file large_files/ # move the large file into the untracked directory
Save your changes
git add path/to/your/large/file # add the deletion to the index
git commit -m 'delete large file' # commit the deletion
Remove the large file from all commits
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch path/to/your/large/file" \
--prune-empty --tag-name-filter cat -- --all
git push <remote> <branch>
I ran into this with a bitbucket account, where I had accidentally stored ginormous *.jpa backups of my site.
git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all
Relpace MY-BIG-DIRECTORY with the folder in question to completely rewrite your history (including tags).
source: https://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/
Just note that this commands can be very destructive. If more people are working on the repo they'll all have to pull the new tree. The three middle commands are not necessary if your goal is NOT to reduce the size. Because the filter branch creates a backup of the removed file and it can stay there for a long time.
$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/
$ git reflog expire --all
$ git gc --aggressive --prune
$ git push origin master --force
git filter-branch --tree-filter 'rm -f path/to/file' HEAD
worked pretty well for me, although I ran into the same problem as described here, which I solved by following this suggestion.
The pro-git book has an entire chapter on rewriting history - have a look at the filter-branch/Removing a File from Every Commit section.
If you know your commit was recent instead of going through the entire tree do the following:
git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD
This will remove it from your history
git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all
Use Git Extensions, it's a UI tool. It has a plugin named "Find large files" which finds lage files in repositories and allow removing them permenently.
Don't use 'git filter-branch' before using this tool, since it won't be able to find files removed by 'filter-branch' (Altough 'filter-branch' does not remove files completely from the repository pack files).
I basically did what was on this answer:
https://stackoverflow.com/a/11032521/1286423
(for history, I'll copy-paste it here)
$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/
$ git reflog expire --all
$ git gc --aggressive --prune
$ git push origin master --force
It didn't work, because I like to rename and move things a lot. So some big file were in folders that have been renamed, and I think the gc couldn't delete the reference to those files because of reference in tree objects pointing to those file.
My ultimate solution to really kill it was to:
# First, apply what's in the answer linked in the front
# and before doing the gc --prune --aggressive, do:
# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit
# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit,
# so we remove all the references.
# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/
# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --prune --aggressive
My repo (the .git) changed from 32MB to 388KB, that even filter-branch couldn't clean.
git filter-branch is a powerful command which you can use it to delete a huge file from the commits history. The file will stay for a while and Git will remove it in the next garbage collection.
Below is the full process from deleteing files from commit history. For safety, below process runs the commands on a new branch first. If the result is what you needed, then reset it back to the branch you actually want to change.
# Do it in a new testing branch
$ git checkout -b test
# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD
# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test
# Remove test branch
$ git branch -d test
# Push it with force
$ git push --force origin master
NEW ANSWER THAT WORKS IN 20222.
DO NOT USE:
git filter-branch
this command might not change the remote repo after pushing. If you clone after using it, you will see that nothing has changed and the repo still has a large size. this command is old now. For example, if you use the steps in https://github.com/18F/C2/issues/439, this won't work.
You need to use
git filter-repo
Steps:
(1) Find the largest files in .git:
git rev-list --objects --all | grep -f <(git verify-pack -v .git/objects/pack/*.idx| sort -k 3 -n | cut -f 1 -d " " | tail -10)
(2) Start filtering these large files:
git filter-repo --path-glob '../../src/../..' --invert-paths --force
or
git filter-repo --path-glob '*.zip' --invert-paths --force
or
git filter-repo --path-glob '*.a' --invert-paths --force
or
whatever you find in step 1.
(3)
git remote add origin git#github.com:.../...git
(4)
git push --all --force
git push --tags --force
DONE!!!
You can do this using the branch filter command:
git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
When you run into this problem, git rm will not suffice, as git remembers that the file existed once in our history, and thus will keep a reference to it.
To make things worse, rebasing is not easy either, because any references to the blob will prevent git garbage collector from cleaning up the space. This includes remote references and reflog references.
I put together git forget-blob, a little script that tries removing all these references, and then uses git filter-branch to rewrite every commit in the branch.
Once your blob is completely unreferenced, git gc will get rid of it
The usage is pretty simple git forget-blob file-to-forget. You can get more info here
https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/
I put this together thanks to the answers from Stack Overflow and some blog entries. Credits to them!
Other than git filter-branch (slow but pure git solution) and BFG (easier and very performant), there is also another tool to filter with good performance:
https://github.com/xoofx/git-rocket-filter
From its description:
The purpose of git-rocket-filter is similar to the command git-filter-branch while providing the following unique features:
Fast rewriting of commits and trees (by an order of x10 to x100).
Built-in support for both white-listing with --keep (keeps files or directories) and black-listing with --remove options.
Use of .gitignore like pattern for tree-filtering
Fast and easy C# Scripting for both commit filtering and tree filtering
Support for scripting in tree-filtering per file/directory pattern
Automatically prune empty/unchanged commit, including merge commits
This works perfectly for me : in git extensions :
right click on the selected commit :
reset current branch to here :
hard reset ;
It's surprising nobody else is able to give this simple answer.
git reset --soft HEAD~1
It will keep the changes but remove the commit then you can re-commit those changes.

How can I get the name of the last person who committed on a remote?

I know that the option -force-with-lease allows me to push force only if I was last committer but I would like to allow overriding it:
git push playground $current_branch:master --force-with-lease
if ! [[ "$?" == "0" ]]; then
last_committer="$(git some command)"
ask_continue "the last committer was $last_committer, would you like to push force?"
git push playground $current_branch:master --force
fi
In that example, I would like to know what git some command would be.
git show --quiet --pretty=format:%an
--quiet - suppresses the diff output
%an - is the author name (For all other formats click this)
If you want to look who made last commit on a specific branch
git show --quiet --pretty=format:%an origin/branch-name
In order get the last commit details for author-name, date and time is
git log -1
Here you can find more detail for getting commit history.
https://git-scm.com/book/en/v2/Git-Basics-Viewing-the-Commit-History
hope this will help you.

Error when creating all branches at once

I have created this bash script to create all branches at once
#!/bin/bash
git fetch -vp
for b in $(git branch -a | grep remotes | grep -v HEAD)
do
branchname=${b##*/}
remote=${b#*/}
command="git branch --track $branchname $remote"
echo "$command"
$($command)
done
but I am always having the same error:
fatal: 'master' is not a valid branch name.
If I run the same command without the script the it is executed successfully.
What am I doing wrong ?
I have created this script because I need push all my branches to another remote repo, so first I need create the all local branches from original repository...
You don't.
You can use the remote tracking branches which you already have from git fetch. That's like origin/master. git fetch has already downloaded all the commits from the remote, and branches in Git are just labels on commits. Even remote branches.
You can get a list of all your remote tracking branches from git branch -r but that's from all remotes. To get the branches for just one remote use git ls-remotes --heads <remote>. The format is a little funny, so you have to do some massaging.
$ git ls-remote --heads $REMOTE | cut -f2 | sed -E "s/refs\/heads/$REMOTE/"
origin/80_method_methods
origin/gh-pages
origin/io-all
origin/issue/217
origin/issue/255
origin/master
origin/rewrite_cmd_wrapper
Then you can push from those branches.
Though wanting to push all the branches from one repo to another is very odd. There's probably a simpler way to solve whatever problem you're trying to solve. I suspect you have an XY Problem.
I would suggest asking a question about that problem instead.
As it is well explained in #Schwern's answer, wanting to "checkout all branches" of a remote repo is probably a XY problem, and it is unneeded in practice because git fetch $REMOTE is enough to retrieve the whole history of the considered repo.
However the proposed command git ls-remote ... does not seem to be appropriate because:
it needs a network connection
it doesn't display the list of remote branches
it just queries the remote repo (without relying on fetched data) to know if the commits corresponding to local branches happen to be known in the remote.
In order to efficiently get the list of all branches from a given remote, I propose instead:
git for-each-ref --format "%(refname:strip=2)" "refs/remotes/$REMOTE/" | grep -v -e '/HEAD$'

Bash conditional based on whether a file has changed since last pull

I'm writing a script for automatically updating a system of ours. Basically I want to do a pull and update from a remote hg repository, and then run some update scripts. Now the problem is that these update scripts takes a while to run and most of them only has to be run if there has been changes to their configurations.
Now my script looks like following:
if hg pull -u
then
run scripts
fi
What I want is something like
if hg pull -u && 'some changes was introduces in my/configuration/directory/*'
then
run scripts
fi
Any idea how to do this?
With templated output of hg incoming your can check before pull, which files will be modified on pull (if any will be) and will act accordingly
hg incoming --template "{files % '{file}\n'}" | grep SOMESTRING
You can use hg status to get a list of files that have been changed between revisions, for example files modified between tip and it's parent(tip^) that are in my\configuration\directory:
hg status my\configuration\directory\** -m --rev "tip^:tip"
I would recommend to pull, check if those files have been altered from the current revset, update, and then run your scripts if your config has changed. To me that looks easier than trying to store which revset you started with and figure it out after the update. (Note that I'm not great with bash/grep, so this is approximate and untested):
hg pull
cfgChngd = hg status -m my\config\dir\** -m --rev tip | grep "M my\config\"
hg update
if cfgChngd
runAllTheScripts
fi
You can use the status command to get a list of changed files -- also between different revisions:
HERE=`hg log --template '{node}' -r.`
hg pull -u
if hg st --rev $HERE:. | grep -q .
then
run scripts
fi

Emulate subwcrev when using git-svn

I use git-svn to interact with an existing SVN repository that contains some C++ projects. subwcrev.exe is used as a pre-build event to update some strings in a C++ header (svnversion.h). These strings get hardcompiled to form some version information for the resulting binary.
Since subwcrev requires .svn metadata to work, the pre-build event is going to fail when used on the git-svn working copy. So I came up with the following bash script which I use as post-commit and post-checkout hooks for my git repository. The script tries to do the same thing as subwcrev based on the output of git svn info (cached in a local file).
#!/bin/sh
if [ ! -f svninfo ] ; then
git svn info > svninfo
fi
revision=`sed -e "/Revision/!d" -e "s/Revision: \(.*\)/\1/" svninfo`
lastchange=`sed -e "/Last Changed Rev/!d" -e "s/Last Changed Rev: \(.*\)/\1/" svninfo`
# Get the last changed date, extract timestamp, replaces dashes with slashes
changedate=`sed -e "/Last Changed Date/!d" -e "s/Last Changed Date: \(.\{19\}\).*/\1/" -e "s!-!\\\\\\/!g" svninfo`
now=`date "+%Y\/%m\/%d %H:%M:%S"`
gitcommit=`git show --abbrev-commit | sed -n -e "s/commit //p"`
for entry in $( find -name svnversion_template.h ); do
newname=`echo $entry|sed -e "s/_template//"`
sed -e "s/\\\$WCRANGE\\\$/${revision}/" \
-e "s/\\\$WCREV\\\$/${lastchange}-${gitcommit}/" \
-e "s/\\\$WCDATE\\\$/${changedate}/" \
-e "s/\\\$WCNOW\\\$/${now}/" \
-e "s/\\\$WCURL\\\$/local git repo/" \
-e "s/\\\$WCMODS.*\\\$/(true)/" \
-e "s/\\\$WCMIXED.*\\\$/(false)/" \
$entry > `echo $entry|sed -e "s/_template//"`
done
What I cannot really emulate so far is the automatic detection of a local uncommitted changes (based on the last checked out SVN revision) that makes subwcrev so useful.
I am replacing $WCREV$ with the revision number of the SVN repository (as subwcrev would do) but this time I add my abbreviated git commit hash to identify the code I compiled. My question now is: Is there a way to distinguish in a shell script whether my current HEAD differs from the last fetched SVN revision so that I could omit adding the -${gitcommit} part and set $WCMODS$ to false?
If there were some thing like a post-"git svn dcommit" hook, my problem would be solved, too, since then that special hook would create the svnversion.h differently. Can such hook be added somehow?
I don't really get your points, but start to improve your script first.
revision=$(grep -Po "(?<=Revision: ).*" svninfo)
lastchange=$(grep -Po "(?<=Last Changed Rev: ).*" svninfo)
# Get the last changed date, extract timestamp, replaces dashes with slashes
changedate=$(grep -Po "(?<=Last Changed Date: ).{19}" svninfo)
changedate=${changedate//-//}
now=$(date "+%Y\/%m\/%d %H:%M:%S")
Then, in for loop, could you please explain detail, what result you need?
Can you show one sample of svnversion_template.h ?
So it looks like you might have to parse the contents of the git svn info query yourself to get what is normally stored in WCREV. The example results look like this for me:
git svn info
Path: .
URL: http://myurl.com/trunk/myrepo
Repository Root: http://myurl.com
Repository UUID: 15fed3e9-81ce-ef4a-a7da-fc36e3df1edc
Revision: 14106
Node Kind: directory
Schedule: normal
Last Changed Author: myusername
Last Changed Rev: 14106
Last Changed Date: 2015-05-29 10:23:10 -0400 (Fri, 29 May 2015)
Now for the second part of your question, whether or not you can tell if your git HEAD matches the latest svn checkout, you'll need to use the command git diff git-svn command. "git-svn" here is the name of the branch that the git-svn program is maintaining, and if everything is up to date, the results will be empty.

Resources