I'm trying to push head commit to remote with WIP- showing error like
$ git push remote 51447424149c671958a2f76ec1fefb135a5c2cea:WIP-51447424149c671958a2f76ec1fefb135a5c2cea
[which results in]
error: unable to push to unqualified destination: WIP-51447424149c671958a2f76ec1fefb135a5c2cea
The destination refspec neither matches an existing ref on the remote nor
begins with refs/, and we are unable to guess a prefix based on the source ref.
error: failed to push some refs to 'https://github.com/abc'
Any help ?
TL;DR
You probably wanted:
$ git push remote 51447424149c671958a2f76ec1fefb135a5c2cea:refs/heads/WIP-51447424149c671958a2f76ec1fefb135a5c2cea
to create a branch named WIP-51447424149c671958a2f76ec1fefb135a5c2cea. (Be sure you really want to create that name, as it's kind of unweildy. It's valid and there is no problem with it, it's just a heck of a thing to type in.)
Long
What Git is complaining about takes a little bit of explanation:
A branch name is a special form of reference.
A reference is a string starting with refs/. The familiar two kinds of references are branch names and tags. (More about this in just a moment.)
References can be abbreviated ... sometimes, but not always.
Sometimes (wherever it makes sense to Git) you can use a raw hash ID instead of a reference.
git push takes a refspec, which is a pair of references separated by a colon (and optionally the whole thing can be prefixed with a plus sign).
What you're doing with git push is using the (very long) refspec 51447424149c671958a2f76ec1fefb135a5c2cea:WIP-51447424149c671958a2f76ec1fefb135a5c2cea. The left side of this refspec is the source reference, and the right side is the destination reference.
The thing on the left side of the colon is clearly1 a hash ID. So this is making use of the special case where you can supply a hash ID instead of an actual reference (as long as that object actually exists in your Git repository).
The thing on the right side of the colon, though, is a name, not a hash ID. This is good since this is one of the places that Git requires a name. But it's also a problem, because the name WIP-something does not start with refs/.
Note that Git explicitly complains about that:
The destination ... nor begins with refs/
Before we get to the rest, let's mention branches and tags again. A branch name like master is short-hand for the reference refs/heads/master. A tag name like v1.2 is short-hand for the reference refs/tags/v1.2. Note that in both cases, these start with refs/. They go on to name which kind of reference we're using:
A branch name reference starts with refs/heads/.
A tag name reference starts with refs/tags/.
In other words, when we say that branches and tags are forms of references, we're saying that given a reference, you can look at what comes right after refs/ and figure out what kind of reference it is: refs/heads/ means "branch" and refs/tags/ means "tag". (If you see refs/remotes/, that means it's a remote-tracking name; and there are yet more special words that go after refs/, such as notes/ for git notes.)
We also said above that references can sometimes be abbreviated. That's the first part of what Git is complaining about here, though:
... neither matches an existing ref on the remote ...
You're allowed to leave out the refs/heads/ part, and have the other Git—the one that your Git is pushing-to—figure out that master really means refs/heads/master. But this only works if they already have a refs/heads/master. If you're trying to create a new branch, you must tell the other Git: I'd like you to create a new branch.
You do this by giving the full name of the reference: refs/heads/WIP-something, for instance. The fact that it starts with refs/heads/ tells the other Git: I'd like to create a branch name. If you send them refs/tags/WIP-something, you are telling them to create a new tag name.
Anyway, this is why you're getting the rather long complaint, with its two parts: "neither ... nor". So the solution is to send them the full name.
1What, isn't it obvious? :-) This reminds me of the professors who prove theorems by doing six transformations and then saying "the rest is obvious...".
Git 2.21 (Q1 2019, 1 year later) will improve that error message: "git push $there $src:$dst" rejects when $dst is not a fully qualified refname and not clear what the end user meant.
The codepath has been taught to give a clearer error message, and also guess where the push should go by taking the type of the pushed object into account (e.g. a tag object would want to go under refs/tags/).
Note: DWIM (used below) is "do what I mean":
computer systems attempt to anticipate what users intend to do, correcting trivial errors automatically rather than blindly executing users' explicit but potentially incorrect inputs.
See commit 2219c09, commit bf70636, commit dd8dd30, commit 04d1728, commit c83cca3, commit 8b0e542, commit cab5398 (13 Nov 2018) by Ævar Arnfjörð Bjarmason (avar).
(Merged by Junio C Hamano -- gitster -- in commit 0a84724, 04 Jan 2019)
push: improve the error shown on unqualified <dst> push
Improve the error message added in f8aae12 ("push: allow
unqualified dest refspecs to DWIM", 2008-04-23, Git v1.5.5.2), which before this
change looks like this:
$ git push avar v2.19.0^{commit}:newbranch -n
error: unable to push to unqualified destination: newbranch
The destination refspec neither matches an existing ref on the remote nor
begins with refs/, and we are unable to guess a prefix based on the source ref.
error: failed to push some refs to 'git#github.com:avar/git.git'
This message needed to be read very carefully to spot how to fix the error, i.e. to push to refs/heads/newbranch.
Now the message will look like this instead:
$ ./git-push avar v2.19.0^{commit}:newbranch -n
error: The destination you provided is not a full refname (i.e.,
starting with "refs/"). We tried to guess what you meant by:
- Looking for a ref that matches 'newbranch' on the remote side.
- Checking if the <src> being pushed ('v2.19.0^{commit}')
is a ref in "refs/{heads,tags}/". If so we add a corresponding
refs/{heads,tags}/ prefix on the remote side.
Neither worked, so we gave up. You must fully qualify the ref.
error: failed to push some refs to 'git#github.com:avar/git.git'
This improvement is the result of on-list discussion in the thread "Re: [PATCH 2/2] push: add an advice on unqualified <dst> push" comment #1 (Oct. 2018) and comment #2, as well as my own fixes / reformatting / phrasing on top.
The suggestion by Jeff on-list was to make that second bullet point "Looking at the refname of the local source.".
The version being added here is more verbose, but also more accurate.
Saying "local source" could refer to any ref in the local refstore, including something in refs/remotes/*. A later change will teach guess_ref() to infer a ref
type from remote-tracking refs, so let's not confuse the two.
And that is not all: there is a new setting now.
Now with advice.pushUnqualifiedRefName=true (on by default) we show a
hint about how to proceed:
$ ./git-push avar v2.19.0^{commit}:newbranch -n
error: The destination you provided is not a full refname (i.e.,
starting with "refs/"). We tried to guess what you meant by:
- Looking for a ref that matches 'newbranch' on the remote side.
- Checking if the <src> being pushed ('v2.19.0^{commit}')
is a ref in "refs/{heads,tags}/". If so we add a corresponding
refs/{heads,tags}/ prefix on the remote side.
Neither worked, so we gave up. You must fully qualify the ref.
hint: The <src> part of the refspec is a commit object.
hint: Did you mean to create a new branch by pushing to
hint: 'v2.19.0^{commit}:refs/heads/newbranch'?
error: failed to push some refs to 'git#github.com:avar/git.git'
When trying to push a tag, tree or a blob we suggest that perhaps the user meant to push them to refs/tags/ instead.
The config has a new advice:
pushUnqualifiedRefname:
Shown when git push gives up trying to guess based on the source and destination refs what remote ref namespace the source belongs in, but where we can still suggest that the user push to either refs/heads/* or refs/tags/* based on the type of the source object.
Related
I am collaborating with my friend on an iOS app. We use different Apple IDs in our Xcodes, so in "Signing and Capabilities" tab of project settings, we select different teams in the "Team" field:
From my observation, changing this affects the MyProject.xcodeproj/project.pbxproj file, which stores the file references that the Xcode project has, in addition to the "Team". Here's a snippet of what is changed:
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = <my team ID>; /* this is changed */
INFOPLIST_FILE = MyProject/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"#executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = io.github.sweeper777.MyApp;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
The problem arises, when one of us commits this file and the other person pulls. The "puller" will now have the "Team" set to something invalid. When this person then tries to run the app on a real device, there will be code signing errors for obvious reasons. To solve this, this person must tediously go through all the targets that we have, and set each "Team" to their own team.
How can we make it so that on each person's computer, the "Team" stays the same after pulling, but any other changes to MyProject.xcodeproj/project.pbxproj is applied?
Remarks:
Putting the entire MyProject.xcodeproj/project.pbxproj in .gitignore doesn't work, because that would ignore every other change to it. Adding a new file to the project, for example, also changes MyProject.xcodeproj/project.pbxproj, and we want to be able to pull that change.
Manually deselecting the lines that say "DEVELOPMENT_TEAM = ..." when committing is as tedious as reselecting the correct team every time, so that's not a solution.
I found this. Apparently, I can configure git to run sed before git checkout and git add. However, that answer seems ignore the line by deleting it completely. This means that my friend, when he pulls, would still have to reselect the correct team. What I want is the kind of "ignore" that simply stops tracking that line. That is, if there is a local version of that line, use that.
I am also aware that this all wouldn't be a problem if we are on the same team. But if I understand this correctly, I can't have multiple people on my team unless I have a Company account, and not only can I not afford that, I don't own a Company.
I don't use Xcode itself and do not know how to smuggle Git hooks and scripts past the Xcode interface, so you'll need more than just this answer. But you mention sed in comments, and given your proposed file format, that may well be the way to go:
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = <my team ID>; /* this is changed */
INFOPLIST_FILE = MyProject/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"#executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = io.github.sweeper777.MyApp;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
Git has the ability to run what it calls clean and smudge filters. These can be used to run any arbitrary program you like, including sed, the "stream editor", which is particularly good at making single-line changes based on regular expression matches.
There is another method that may also work, and may "play better" with Xcode, or may play worse. I'll go over that too, after covering clean and smudge filters.
Before we dive into writing clean and smudge filters, and using them from Git—you'll need to know all of these details as you will have to write your own custom filters—we should start with a simple fact about Git commits: No part of any commit can ever be changed. Once you make a commit, the stuff that's inside the commit—the stored data in all of its files—is the way it is, forever. So these filters have to work within that system. Remember that, as it will help with understanding what we're doing.
How Git makes and stores objects
The files inside a commit are not files, exactly: they're not the same thing as files in your file system, at least. Instead, they are what Git calls objects, specifically blob objects. A blob object holds the file's data; other objects hold the file's name; and commit objects collect everything together to be used all at once. There's one more internal object type for annotated tags but we'll stop here as we're really only interested in the blob-object part.
When Git extracts a commit, it reads the internal blob objects and runs them through internal code to decompress and format them into regular files. This can include doing end-of-line hacking (turning LFs into CRLFs) if desired. Normally, all this happens entirely inside Git, and the end result is that Git writes out an ordinary everyday file for you to use. This ordinary file is what you will work on / with, in Xcode or any other editor and compiler system and so on. These ordinary files are in your working tree.
After you've extracted some commit, you'll do some work on it, by changing some or all of the files in your working tree, to achieve whatever result you wanted. This can include changing the buildSettings, editing Swift code, editing Objectionable-C Objective-C code, and so on. You might add all-new files to the working tree, some of which you never commit at all (you can help make sure this never happens by listing such files in .gitignore).
Eventually, though, you'd like to commit the updated code. To do so, you must run git add, or maybe have your IDE run git add for you (perhaps Xcode has clicky buttons to do this). This invokes code in Git that converts the working tree file(s) back to internal blob objects if and as needed.
Again, normally this is all handled entirely inside of Git. Git will read the working tree file, maybe do CRLF-to-LF-only changes, compress the text, search for duplicate objects, and do all the other complicated things necessary to prepare the file, so that it is ready to be committed. The resulting data need not match what's in your working tree at all: it just has to be something that, when Git later goes to extract the file, produces what you will need in your working tree.
Clean and smudge filters
This is where these clean and smudge filters come in. I said, above, that normally, Git does the extraction and insertion all on its own. For binary files, the only thing Git does here is apply lossless compression.1 For text files, Git can do CRLF/LF substitutions as well. But what if you'd like to do your own operations?
You can: Git will let you do whatever you want during the extract process with a smudge filter, and will let you do whatever you want during the compress process with a clean filter. The clean filter replaces the in-file data, using a stream-edit type process,2 and then Git does its CRLF hacking if any and compressing on the "cleaned" data. The smudge filter replaces the decompressed, post-CRLF-hacking data coming out of Git with the data that should go into the working tree.
Hence you can write, as your clean filter, a sed script of the form:
s/DEVELOPMENT_TEAM = .*;/DEVELOPMENT_TEAM = DEVTEAMTEMPLATE;/
With that as the entire sed script, what sed will do is edit the incoming data stream and replace any actual development team text with the word DEVTEAMTEMPLATE.
Your smudge filter has to work slightly harder: it must find the template line and adjust it so that it contains the correct team ID. Where will you get the correct team ID? That's up to you: perhaps you can store it in a file in your home directory, or in a file that you create in the working tree but never commit in Git. You'll have to write this one or two or however-many-liner sed and/or shell script yourself.
1There are multiple phases of compression; git add does just one, and git checkout undoes all—including reading from "pack" files—as needed. The deeper level of compression, using delta encoding techniques, is entirely invisible at the "object" level, so nobody ever really has to think about it.
2With the advent of Git-LFS, Git gained the ability to run long-lived filters. Before that, Git always used simple stream filtering. The stream filtering is easier to understand, but is less efficient for doing en-masse operations on many files. Here, we're only interested in one file per repository anyway, so there's no need to go into the fancier long-lived filter details.
Defining clean and smudge filters
The tricky part here, with Git, is that you must define the filters in one place—in $HOME/.gitconfig or .git/config, for instance—and then tell Git to invoke them from another place, using the .gitattributes file. This is described in the gitattributes documentation. This documentation is pretty thorough, so read it. You can ignore all the long-running filter discussion, as noted above. I will quote one bit from the documentation here for emphasis, though, and expound on it:
Note that "%f" is the name of the path that is being worked on. Depending on the version that is being filtered, the corresponding file on disk may not exist, or may have different contents. So, smudge and clean commands should not try to access the file on disk, but only act as filters on the content provided to them on standard input.
When Git is running the smudge filter, it:
has opened some internal object (which may or may not be packed);
has decompressed it, or is in the process of decompressing it, and pumped / is-pumping out the data; and
this data is being fed to your filter, but is not written out to any file anywhere.
Your filter can use %f to know the name of the target output file, but the data are not in that file yet. The data bytes are only in some OS-level pipes or sockets or whatever your OS uses for connecting the output of one program (Git's internal decompressors) to another (your filter). Your smudge filter must read its standard input to get the data, and write the smudged data to standard output so that Git can read it (if necessary) and/or redirect that output to the correct file. Do not attempt to open the file by name!
(The same holds for the clean filter, except that in many cases, the input to your filter is just the raw data already in the file, so that opening the file and reading it mostly works. So this can mislead you, if you do your tests using a clean filter.)
Note that you can implement this scheme without a clean filter at all: your smudge filter can replace whatever is in the committed file even if it's a real team ID, rather than just a template. If you choose to do this, however, you'll "see" the team ID changing every time a different team-ID commits the file. The nice thing about using the clean filter is that once the committed copies of the file use the template line, every future cleaned file also uses the template line, so that it never changes.
Alternative: a template file
In general, it's unwise to commit actual configurations. Clean and smudge tricks can work, but they can only go so far: this particular file format works well because the change you want made is on a single line, and Git itself shows you file changes on a line-by-line basis, and sed works well with line-oriented input, and so on.
A lot of configuration files, though, wind up storing at least slightly-sensitive data, or perhaps very-sensitive data such as cleartext passwords. Such files should not be stored in Git at all if at all possible. Instead, you would store a template file in Git.
In this case, for instance, instead of storing MyProject.xcodeproj/project.pbxproj, you might have Git store MyProject.xcodeproj/project.pbxproj.template. This file would have template-ized contents. When you clone and check out the repository, you'd subsequently copy the template file into place and do any required adjustments.
Should the MyProject.xcodeproj/project.pbxproj file itself need to change, e.g., to acquire a new SWIFT_VERSION setting, you'd instead edit the template file, add that to Git, and commit. You would then use the usual "convert template to mine" process, or manually update the MyProject.xcodeproj/project.pbxproj file. Since this file is never committed—and is listed in .gitignore—it never goes into any commit and you never have to worry about collisions within it. Only the template file goes into Git.
Let's say I have a branch called parent-branch and I create a branch right off of that branch by doing
git checkout -b child-branch parent-branch
That's all fine and well of course, but what I am looking (hoping) to do, is to be able to somehow reference parent-branch from within a bash script. So for example, something like git_current_branch and git_main_branch will print the current local branch I am in, and print the master branch, respectively.
Is there a way where I can do something like git_parent_branch (or something along those lines) to have access to the parent-branch in bash and the command line. Whether that be a bash script function, or whatever other potential possibilities might work.
Is there something involving GitHub and / or any associated APIs perhaps where this is possible. I'm not overly familiar with connecting to GitHub other than just using their web interface, so anything in that respect would most likely be of big help (ideally pertaining to my issue here)!
In Git at a fundamental level, branches simply don't have parents. Well, I say simply, but it's not that simple, because we haven't defined branch, and users of Git use the word very loosely and often mean different—and contradictory, sometimes—things when they say it. So let's define branch name first, which at least has a simple, definite meaning:
A branch name is a name whose full spelling starts with refs/heads/, which—in order to exist—contains the hash ID of some existing, valid commit.
The last bit here—which is kind of redundant: an existing commit is valid, and a valid commit (whatever that means) must exist—is a concession to the fact that we can have branch names that don't exist (yet, or any more): xyzzy, for instance, is fine as a branch name, but until you create it, it's just a sort of potential branch name, floating in limbo as it were.
Because a branch name must contain a commit ID to exist, a new, empty repository—which has no commits—has no branch names either. And yet you're on the initial branch. It's in limbo, as yet nonexistent. When you make your first commit in this empty repository, then the branch name actually exists. If you like, you can re-create this special case in a non-empty repository using git checkout --orphan or git switch --orphan. (These are subtly different in how they manipulate Git's index, but both put you in this funky state of being on a branch that does not yet exist.)
This kind of special case aside, because a branch name has to contain some commit hash ID, we normally create a branch by picking some existing hash ID, just as in your example:
git checkout -b child-branch parent-branch
But what Git does with this is to resolve the name parent-branch to a commit hash ID first, then create a new branch—in this case, named child-branch—containing that hash ID. The two branch names have no parent/child relationship; we could run git checkout -b daddy kid or git checkout -b xyzzy plugh and there's no parent/child relationships here either, despite the misleading name in the daddy kid version and the neutral names in the xyzzy plugh case.
Now we come to your own question, though:
Is there a way where I can do something like git_parent_branch (or something along those lines) to have access to the parent-branch in bash and the command line.
Git contains, as a useful tool—parts of Git make use of this in various ways—a fully general string-based configuration system, where we run git config to set some arbitrary string to some arbitrary value. By convention, these strings have a hierarchical structure: user.name and user.email live within the user space; push.default is composed of push + default; and so on. Git even stores them using an INI-file-style syntax.
What this means is that although Git itself has no parent/child relationship, you can make up your own. There are a few obvious drawbacks to doing so:
Git won't maintain it for you.
You need to choose names that Git won't clobber, even in some future release (Git version 3.14 perhaps).
Nobody else will understand what the heck you're doing.
So, if you choose to do this, you're on your own—but let's note that Git does store some per-branch information in the branch.name namespace:
branch.xyzzy.remote is the remote setting for the branch named xyzzy;
branch.xyzzy.rebase is the git pull setting controlling whether the second command to use is git merge or git rebase, and depending on which second command is to be used, what flags, if any, to pass to that second command, when you're on branch xyzzy and you run git pull;
branch.xyzzy.description is the descriptive text that git format-patch will include in a cover letter, when run for branch xyzzy;
and so on. So if you were to add a branch.name.parent string value, you could store your string here. You then merely need to hope that the Git developers don't steal that name—parent—from you in the future.
Since this stuff is totally free-form, you'd just run git symbolic-ref or similar to find the current branch name, then git config --get branch.$branch.parent to get its parent setting, if it has one. If it does not have one, this must be a normal everyday parentless Git branch, rather than one of your own specially decorated branches that does have a nominal parent. To set the parent for some branch, you'd run git config branch.$branch.parent $parent, where $parent is the setting you want. (It's your decision as to whether $parent is required to be a branch name, in which case strings like xyzzy and main and plugh are fine, or whether it could be a remote-tracking name as well, in which case, you'd better use fully-qualified strings like refs/heads/xyzzy, refs/heads/main, and so forth. That will allow you to use refs/remotes/origin/main—a remote-tracking name—as a "parent".)
Is there something involving GitHub and / or any associated APIs perhaps where this is possible.
Definitely not, and this points up another weakness in the idea of using branch.$name.parent: there is no way to record this data on GitHub. It's a purely local setting. Then again, branch names are purely local: there's noting that requires that you call your development branch dev or develop, even if the development branch name in some GitHub repository you've cloned is called dev or develop.
Before I finish this off, let me add another several definitions of branch. We'll needs a few more definitions as well:
A branch tip is the commit to which a branch name points. That is, given some branch name like main that indicates some particular commit hash ID such as a123456..., the tip commit of branch main is a123456.... Checking out a branch by its name—with git checkout or git switch—and then adding a commit automatically stores the new commit's hash ID in the branch name, so that the tip commit automatically advances. The new commit's parent will be the old branch tip.
A branch (in one of its many meanings) is a set of commits that includes the tip commit of a branch (with branch here meaning name that contains a commit hash ID). Where this set of commits begins is in the mind of the user, but if left unspecified, Git generally includes every commit reachable from the tip commit.
To define reachable, see Think Like (a) Git.
A remote-tracking name is a name that exists in your Git repository but was created due to a branch name that your Git saw in some other Git repository. These names live in the refs/remotes/ namespace, which is further qualified by the remote, such as origin. For instance, refs/remotes/origin/main would be a remote-tracking name in your repository, in which your Git remembers the hash ID stored in origin's branch name main, the last time your Git got an update from their Git.
For some users, a remote branch is a branch (in the meaning of series of commits terminating at a tip commit) where the tip commit is given by a remote-tracking name. For other users—or the same user speaking at some other time—a remote branch is a branch that exists in some remote repository, such as origin. These two are easily conflated since your own origin/main tracks the other Git's main, hence the term remote-tracking name. (Git calls this a remote-tracking branch name, but the adjective remote-tracking in front of the noun name seems sufficient here.)
As you can see, the word branch is so loosely defined as to be nearly valueless. We can often reconstruct the correct definition—the one a speaker or writer had in mind—based on context, but for clarity, it's often better to use some other term.
I am versioning Microsoft Access VBA code, which is in general case insensitive. However changes the case of variable names happen every now in then (by the Access compiler or by the developer). This often leads to huge change set in my git workspace.
How can I revert or ignore changes, that only concern upper- or lowercase of file contents?
An example:
git init
echo "public sub example()\nend sub" > mdlExample.ACM
# ^-- lower e
git add --all
git commit --all --message "Initial Commit"
echo "public sub Example()\nend sub" > mdlExample.ACM
# ^-- upper E
I would love something like:
git restore --only-case-changes # not working
And then:
git status
> On branch master
> nothing to commit, working tree clean
Consider changing example="example" to Example="Example". How do you propose Git could decide which case change to ignore here? Now consider code snippets in comments, or stored as strings for code generators. I get wanting Git to make an annoying chore go away, but I think if you try to imagine telling Git exactly what you want you'll understand the context of your question a little better.
How can I revert or ignore changes, that only concern upper- or lowercase of file contents
When you want to temporarily ignore changes, when you want to do a diff or a blame without seeing those changes, you can use a "textconv" filter that normalizes the text you diff. I use those to do things like strip embedded timestamps out of generated html when diffing, quickest to hand atm is
[diff "doc-html"]
textconv = "sed 's,<span class=\"version\">Factorio [0-9.]*</span>,,;s,<[^/>][^>]*>,\\n&,g'"
wordRegex = "<[^>]*\\>|[^< \\t\\n]*"
in .git/config, and
doc-html/*.html diff=doc-html
*.cfg -diff
in .git/info/attributes.
so my what-changed diffs don't show me things I don't care about.
If you want to see the results of a diff ignoring case, try
[diff "nocase"]
textconv="tr A-Z a-z"
and drop * diff=nocase (or maybe*.vba diff=nocase) into .git/info/attributes. When you're done, take it out.
but for merging, my leadoff example should convince you that Git automatically and silently making case changes in repo content, even just in the text that looks like identifiers, is a Bad Idea. When there's a conflict, not just a one-sided change but two different changes, it's still going to take some human judgement to decide what the result should be. Fortunately, with any decent merge tool, resolving simple conflicts is down around subsecond range each.
You don't have to git restore anything: You could setup a clean content filter driver as illustrated here, which will automatically convert those cases on git diff/git commit.
That means:
you won't even see there is a diff
you won't add /commit anything because of that content filter driver.
Image from "Keyword Expansion" section of the "ProGit book"
This is done through:
a .gitattributes filter declaration, which means you can associate it only to certain files (through, for instance, their extension)
*.ACM filter=ignoreCase
a local git config filter.<driver>.clean to declare the actual script (which should be in your PATH)
git config filter.ignoreCase.clean ignoreCase.sh
# that give a .git/config file with:
[filter "ignoreCase"]
clean = ignoreCase.sh
The trick is:
Can you write a script which takes the content of an ACM file as input and produces the same ACM file as output, but with its strings converted?
You can have the filename in those scripts so you can do a diff and detect if said difference has to be adjusted, but you still need to write the right command to replace only "xxx" strings when their case changes in ACM files.
Note: jthill suggests in the comments to set the merge.renormalize config settings to tell Git that canonical representation of files in the repository has changed over time.
Have you considered the answer from this StackOverflow question:
How to perform case insensitive diff in Git
Maybe you can write a script to go and do the diff comparison for each commit and then add those commits to your branch. It may not be as simple as you like but maybe it will simplify the display of the changes to allow you to get to the case insensitive changes quicker?
I want to have part of my destination path in build.cake be based on the branch being built. The top of my build.cake script says:
var branch = Argument("branch", "Master");
and I modified my Team City Build step to include
.\build.ps1 ... -Branch %teamcity.build.branch%
but Team City complains that I have no eligible agents
Implicit requirements: teamcity.build.branch defined in Build step:
Build
I'm new to TC and Cake so I'm likely missing something obvious. How do I hook this up?
You are correct that %teamcity.build.branch% will give access to your branch name. However, this variable is blank unless you have a branch specification in your VCS root settings. In order to use say master as your build.branch parameter you need to add: +:refs/heads/(master) to your branch specification. Whatever is in between the parens will be put in the build.branch variable. If you might be building from multiple branches, you might have something like the following:
+:refs/heads/Release/(765/1.0)
+:refs/heads/Release/*
This would give your build.branch the name of branch as it appears in git after the Release/.
Also see Johan's answer: https://stackoverflow.com/a/27829516/6222375
We are using git at work for a large team (>100 developers) and I am writing different scripts to provide git statistics to management.
One of the statistic that management wants to know is when commit was actually pushed to the repository. They don't really care about author date or committer date because what is matter is when the commit was pushed and therefore picked up by CI server. So I had to implement a thing like push date. Just for completeness (not to advertise myself :)) here is my blogpost describing the details http://mnaoumov.wordpress.com/2013/01/31/git-get-push-date/
Basically I use custom git notes to store details when the commit was actually pushed to the remote repository.
Let's consider a simple task: provide list of all commits between A (exclusively) and B (inclusively) and output commit hash, commit message and a push date
Well I can do something like
git log A..B --notes=push-date --format=<begin>%H<separator>%s<separator>%N<end>
And then parse things accordingly. Well this is significantly slow anyway. And also I don't like do a string parsing and I prefer strongly typed approach.
So to solve performance issues and get rid of parsing I decided to use LibGit2Sharp library.
Well if we don't touch notes it works pretty fast but as soon as I try to retrieve notes it becomes very-very slow
# PowerShell script
$pushDateNote = $commit.Notes | Where-Object -FilterScript { $_.Namespace -eq "push-date" }
$pushDate = [DateTime]::Parse($pushDateNote.Message)
For comparison if I don't include notes - results for 200 commits returned in about 2 seconds. If I include notes - time goes up to 2 minutes.
And I've checked that bottleneck here is a search note by a commit. It seems that git itself doesn't have a map between commit and note so it needs to lookup through all the notes all the time.
I've just checked we have 188921 commits in the repository, so the most likely situation will go even worse. So my solution is not scalable at all.
So my question am I doing it wrong? Maybe git is not right tool to store its own metadata efficiently? I am thinking now to move all the metadata into an external database such as MSSQL. But I'd rather keep everything in one place. Alternatively I was thinking to keep whole map between commit and its push date serialized as a note in one commit
For example to use magic hash 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (Is git's semi-secret empty tree object reliable, and why is there not a symbolic name for it?)
git notes add 4b825dc642cb6eb9a060e54bf8d69288fbee4904 -m serialized-data
$serializedData = git notes show 4b825dc642cb6eb9a060e54bf8d69288fbee4904
This will help to retrieve data only once and therefore no lookup issues. But it will add additional overhead to serialize-deserialize data and this just doesn't look right to me.
Please share your thoughts
Accessing the notes from the Commit object makes libgit2 access the notes tree at each iteration of the loop. A more efficient way to do it is to:
first, load the list of commits you are interested in (you are already doing that apparently)
then load all the notes associated with the push-date namespace only once
and eventually perform a join between those two lists
note: this will add some more pressure from a memory perspective, but it should be faster.
This can be done in C# with the following code:
using (var repo = new Repository("your_repo_path"))
{
var notes = repo.Notes["push-date"];
var commits = repo.Commits.QueryBy(
new CommitFilter {Since = "1234567", Until = "89abcde"});
var pairs = from commit in commits
from note in notes
where note.TargetObjectId == commit.Id
select new {Commit = commit, Note = note};
foreach (var pair in pairs)
{
Debug.Write(pair.Commit.Sha + " : " + pair.Note);
}
}
This will output the commits which have a note associated in the push-date namespace.
note: if you are using the QueryBy syntax to retrieve the list of commits, please be aware that commit specified as Until will be excluded from the list (e.g.: as in git log A...B)
In order to also show the commits which have no notes associated in the push-date namespace, you can use the following linq query:
var pairs2 = from commit in commits
join note in notes on commit.Id equals note.TargetObjectId into gj
from subnote in gj.DefaultIfEmpty()
select new { Commit = commit, Note = subnote };
You can always consider using alternatives to 'git notes'.
See: https://www.tikalk.com/posts/2015/11/12/yet-another-way-to-implement-commit-metadata/